├── .firebase └── hosting.YnVpbGQ.cache ├── .firebaserc ├── .gitignore ├── README.md ├── firebase.json ├── functions ├── .gitignore ├── index.js ├── package-lock.json ├── package.json └── ui-debug.log ├── package-lock.json ├── package.json ├── public ├── 404.html ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── robots.txt └── worker.js ├── src ├── App.css ├── App.js ├── Assets │ ├── Fest.png │ ├── Logo.svg │ ├── background.png │ ├── camera.svg │ └── hack.png ├── Auth │ ├── Login.js │ └── Signup.js ├── Components │ ├── Alert │ │ └── index.js │ ├── Cards │ │ ├── AssignmentCard.js │ │ ├── ClassCard.js │ │ ├── CourseCard.js │ │ ├── ForumQuestionCard.js │ │ ├── ResourceCard.js │ │ └── VideoCard.js │ ├── Containers │ │ ├── AssignmentContainer.js │ │ ├── ClassesContainer.js │ │ ├── CourseContainer.js │ │ └── ViewSubmissionContainer.js │ ├── Footer │ │ └── Footer.js │ ├── Forms │ │ ├── AddAdminForm.js │ │ ├── AddAssignmentForm.js │ │ ├── AddClassForm.js │ │ ├── AddCourseForm.js │ │ ├── AddNotificationForm.js │ │ ├── AddQuestionForm.js │ │ ├── AddResourceForm.js │ │ ├── AddVideoForm.js │ │ ├── EditStudentForm.js │ │ ├── EditTeacherForm.js │ │ └── UploadAssignment.js │ ├── Info │ │ ├── Info.js │ │ └── TeacherInfo.js │ ├── Modal │ │ └── index.js │ ├── Navbar │ │ └── Navbar.js │ ├── Notification │ │ └── index.js │ ├── Slider │ │ └── index.js │ └── Table │ │ ├── AdminTable.js │ │ ├── StudentTable.js │ │ ├── SubmissionsTable.js │ │ └── TeacherTable.js ├── Dashboards │ ├── admin │ │ ├── AdminDashboard.js │ │ └── pages │ │ │ ├── Admins.js │ │ │ ├── Assignments.js │ │ │ ├── Course.js │ │ │ ├── Courses.js │ │ │ ├── Overview.js │ │ │ ├── Student.js │ │ │ ├── Students.js │ │ │ ├── Teacher.js │ │ │ └── Teachers.js │ ├── forum │ │ └── StudentForum.js │ ├── student │ │ ├── StudentDashboard.js │ │ └── pages │ │ │ └── Home.js │ └── teacher │ │ ├── TeacherDashboard.js │ │ └── pages │ │ ├── MyAssignments.js │ │ ├── MyClasses.js │ │ └── TeacherOverview.js ├── Errorpage.js ├── Hooks │ └── useForm.js ├── Routes.js ├── Store │ ├── actions │ │ ├── assignmentActions.js │ │ ├── authActions.js │ │ ├── classActions.js │ │ ├── courseActions.js │ │ ├── forumActions.js │ │ ├── notificationActions.js │ │ ├── studentActions.js │ │ └── teacherActions.js │ └── reducers │ │ ├── authReducer.js │ │ ├── coursesReducer.js │ │ ├── rootReducer.js │ │ └── studentReducer.js ├── config │ └── fbConfig.js └── index.js └── yarn.lock /.firebase/hosting.YnVpbGQ.cache: -------------------------------------------------------------------------------- 1 | favicon.ico,1613543104297,eae62e993eb980ec8a25058c39d5a51feab118bd2100c4deebb2a9c158ec11f9 2 | logo192.png,1613543104297,3ee59515172ee198f3be375979df15ac5345183e656720a381b8872b2a39dc8b 3 | logo512.png,1613543104297,ee7e2f3fdb8209c4b6fd7bef6ba50d1b9dba30a25bb5c3126df057e1cb6f5331 4 | robots.txt,1613543104297,bfe106a3fb878dc83461c86818bf74fc1bdc7f28538ba613cd3e775516ce8b49 5 | index.html,1618356686368,a1f61dc615a28feac112398ecf70c92359aba66a4da4ec9aa5b97d9f39661887 6 | asset-manifest.json,1618356686371,f5e8f8f173fcdefb00a55b75729352451a04c5507f8ec72a95d3125e3098333e 7 | static/css/main.4b1710c1.chunk.css,1618356686371,7fbf11dd061295cd434223949686b1eb5e62d591c523df2df7c51ca270b22e96 8 | static/css/main.4b1710c1.chunk.css.map,1618356686396,0bf46ddf9c5c57e9b2c837044c3e917d6e97ffcc85ca733900c2870cc2918030 9 | static/js/2.9aaabe01.chunk.js.LICENSE.txt,1618356686395,db5e990c63366acc28ee4d68a8b06cf6f262336190be423eb925127eea05a96c 10 | static/js/runtime-main.32128dd0.js,1618356686377,c895eadb410e19377ca0e93a9573f5fdc84370570d1cfef903360a26a58772d5 11 | static/js/runtime-main.32128dd0.js.map,1618356686395,b8d9b9714d21429e4986251035918ea299774f6d32cb3915d4694ddb2eb36030 12 | static/media/camera.dd4016c6.svg,1618356686395,8310c1d9d98c7993f6a6b42bd3a8085d8be13171d9ab9a526008f4444d02adf7 13 | static/media/Logo.a00b17f4.svg,1618356686371,0a40b20644a1921749cae8161947f6dd71ce88ceae1ca32cadb34e6570b41d7f 14 | manifest.json,1618344024926,2c5c63743da10d9e03cc448f0b87a754b86b7342b986e40cd804831c2b776589 15 | 404.html,1615468751007,daa499dd96d8229e73235345702ba32f0793f0c8e5c0d30e40e37a5872be57aa 16 | worker.js,1618344024926,a9a27abba09f6c5db60a195502a8be40655a6dfc40f5af7111058ab7092b1f31 17 | static/js/main.ab1331d9.chunk.js,1618356686395,5ddb969b8f81a1b2cf9f66eb2bd2169286bac5aa3834d631f71757da045182e7 18 | static/css/2.8461011e.chunk.css,1618356686395,f9a18b309596300ed4ef5190fa0d8ec908bcd0116cd35134dbe06f4df54f4be5 19 | static/js/main.ab1331d9.chunk.js.map,1618356686395,b8ab40125cccc0ac387c9497d9df576a8db8c6c19de4ba9cf704c467a0411c14 20 | static/css/2.8461011e.chunk.css.map,1618356686396,4c35d3ea6451ab1066fa062005b5292bfa8f70369c0d201527fbe21a4373c9c9 21 | static/js/2.9aaabe01.chunk.js,1618356686396,e2c4b44db33a6295a91d5adf0c829dae0a35492a5150332e6bb60f209d0ba8e1 22 | static/media/background.08e12498.png,1618356686397,3f63ae097e13e9fff1a6054f867a66b655fa45907227a28ccbce7315fa2c3bec 23 | static/js/2.9aaabe01.chunk.js.map,1618356686397,7ebf9cc0d4aaa54d86c5ae32e551e12cc8606267df764d5c47492fd39df9a1cf 24 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "elearning-project-5423b" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.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 | 25 | .vercel -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learning Management System - Project 2 | 3 | ### Tech Stack 4 | 5 | - React.js (create-react-app) 6 | - Bootstrap (reactstrap) 7 | - react-redux-firebase 8 | - react-redux 9 | - redux-firestore 10 | - Firebase 11 | 12 | ### How to run it locally 13 | 14 | 1. Install VS Code. 15 | 2. Clone the repository using github [How to](https://blog.velingeorgiev.com/how-to-clone-git-project-with-visual-studio-code). 16 | 3. In the terminal, enter `npm install` to install all dependencies. 17 | 4. Enter `npm run start` after the dependencies are installed. 18 | 5. The site will be visible on `localhost:3000` 19 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require("firebase-functions"); 2 | const admin = require('firebase-admin'); 3 | const cors = require('cors')({origin: true}) 4 | admin.initializeApp(functions.config().firebase) 5 | 6 | const createNotification = ((notification) => { 7 | return admin.firestore().collection('notifications') 8 | .add(notification) 9 | .then(doc => console.log('notification added', doc)); 10 | }); 11 | 12 | 13 | exports.newNotificationAdded = functions 14 | .firestore 15 | .document('adminnotifications/{adminnotificationId}') 16 | .onCreate( 17 | doc =>{ 18 | const newNotification = doc.data(); 19 | return createNotification(newNotification) 20 | } 21 | ) 22 | 23 | 24 | exports.addAdmin = functions.https.onRequest(async (req, res) => { 25 | cors(req, res, async() => { 26 | try { 27 | const newAdmin = { 28 | email: req.body.email, 29 | password: req.body.password, 30 | } 31 | 32 | const adminRecord = await admin 33 | .auth() 34 | .createUser(newAdmin); 35 | 36 | const userId = adminRecord.uid; 37 | 38 | await admin.firestore().collection("users").doc(userId).set({ 39 | email: req.body.email, 40 | name: req.body.name, 41 | userType: 'Admin', 42 | password: req.body.password, 43 | phone: req.body.phone, 44 | }); 45 | 46 | return { result: 'The new admin has been successfully created.' }; 47 | } catch (error) { 48 | console.log(error) 49 | } 50 | }) 51 | }) -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "12" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "cors": "^2.8.5", 17 | "firebase-admin": "^9.2.0", 18 | "firebase-functions": "^3.11.0" 19 | }, 20 | "devDependencies": { 21 | "firebase-functions-test": "^0.2.0" 22 | }, 23 | "private": true 24 | } 25 | -------------------------------------------------------------------------------- /functions/ui-debug.log: -------------------------------------------------------------------------------- 1 | Web / API server started at http://localhost:4005 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lms", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@carbon/icons-react": "^10.28.0", 7 | "@testing-library/jest-dom": "^5.11.4", 8 | "@testing-library/react": "^11.1.0", 9 | "@testing-library/user-event": "^12.1.10", 10 | "bootstrap": "^4.6.0", 11 | "boxicons": "^2.0.7", 12 | "firebase": "^8.2.9", 13 | "react": "^17.0.1", 14 | "react-bootstrap": "^1.5.0", 15 | "react-dom": "^17.0.1", 16 | "react-hook-form": "^6.15.5", 17 | "react-redux": "^7.2.2", 18 | "react-redux-firebase": "^3.10.0", 19 | "react-redux-firestore": "0.0.1", 20 | "react-router-dom": "^5.2.0", 21 | "react-scripts": "4.0.3", 22 | "react-toastify": "^7.0.3", 23 | "reactstrap": "^8.9.0", 24 | "redux": "^4.0.5", 25 | "redux-firestore": "^0.15.0", 26 | "redux-logger": "^3.0.6", 27 | "redux-thunk": "^2.3.0", 28 | "uid": "^2.0.0", 29 | "video-react": "^0.14.1", 30 | "web-vitals": "^1.0.1" 31 | }, 32 | "scripts": { 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "test": "react-scripts test", 36 | "eject": "react-scripts eject" 37 | }, 38 | "eslintConfig": { 39 | "extends": [ 40 | "react-app", 41 | "react-app/jest" 42 | ] 43 | }, 44 | "browserslist": { 45 | "production": [ 46 | ">0.2%", 47 | "not dead", 48 | "not op_mini all" 49 | ], 50 | "development": [ 51 | "last 1 chrome version", 52 | "last 1 firefox version", 53 | "last 1 safari version" 54 | ] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page Not Found 7 | 8 | 23 | 24 | 25 |
26 |

404

27 |

Page Not Found

28 |

The specified file was not found on this website. Please check the URL for mistakes and try again.

29 |

Why am I seeing this?

30 |

This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.

31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashankbhat2/lms/f40dd5f6c42ac1a87dbf1b73ae5a8cf64c85c09e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | AcadOnline - LMS 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashankbhat2/lms/f40dd5f6c42ac1a87dbf1b73ae5a8cf64c85c09e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashankbhat2/lms/f40dd5f6c42ac1a87dbf1b73ae5a8cf64c85c09e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "AcadOnline - LMS", 3 | "name": "AcadOnline - LMS", 4 | "icons": [ 5 | { 6 | "src": "./logo192.png", 7 | "type": "image/png", 8 | "sizes": "192x192" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff", 15 | "prefer_related_applications":false 16 | } 17 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/worker.js: -------------------------------------------------------------------------------- 1 | 2 | var CACHE_NAME = 'pwa-task-manager'; 3 | var urlsToCache = [ 4 | '/', 5 | '/completed' 6 | ]; 7 | 8 | // Install a service worker 9 | self.addEventListener('install', event => { 10 | // Perform install steps 11 | event.waitUntil( 12 | caches.open(CACHE_NAME) 13 | .then(function(cache) { 14 | console.log('Opened cache'); 15 | return cache.addAll(urlsToCache); 16 | }) 17 | ); 18 | }); 19 | 20 | // Cache and return requests 21 | self.addEventListener('fetch', event => { 22 | event.respondWith( 23 | caches.match(event.request) 24 | .then(function(response) { 25 | // Cache hit - return response 26 | if (response) { 27 | return response; 28 | } 29 | return fetch(event.request); 30 | } 31 | ) 32 | ); 33 | }); 34 | 35 | // Update a service worker 36 | self.addEventListener('activate', event => { 37 | var cacheWhitelist = ['pwa-task-manager']; 38 | event.waitUntil( 39 | caches.keys().then(cacheNames => { 40 | return Promise.all( 41 | cacheNames.map(cacheName => { 42 | if (cacheWhitelist.indexOf(cacheName) === -1) { 43 | return caches.delete(cacheName); 44 | } 45 | }) 46 | ); 47 | }) 48 | ); 49 | }); 50 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | *{ 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body{ 8 | margin:0px; 9 | font-family: 'Source Sans Pro' !important; 10 | height:100%; 11 | } 12 | 13 | .welcome-card{ 14 | font-weight: 600; 15 | width: 100%; 16 | padding: 20px; 17 | height: auto; 18 | border: none !important; 19 | box-shadow: 1px 4px 12px rgba(3, 0, 159, 0.1); 20 | } 21 | .welcome-card > h2{ 22 | color: #4f4f4f; 23 | font-family: 'Source Sans Pro'; 24 | } 25 | .username{ 26 | color: #03009F; 27 | font-family: 'Source Sans Pro'; 28 | font-size: 35px; 29 | font-weight: 550; 30 | margin: 0 5px; 31 | } 32 | 33 | .class-helper{ 34 | color: #4f4f4f; 35 | text-align: center; 36 | } 37 | .class-link{ 38 | color: white; 39 | text-decoration: none !important; 40 | } 41 | .class-link:hover{ 42 | color: white; 43 | } 44 | .class-card{ 45 | font-family: 'Source Sans Pro'; 46 | box-shadow: 2px 4px 10px rgba(0, 0, 0, 0.1); 47 | border: none !important; 48 | padding: 20px; 49 | } 50 | .class-btn{ 51 | text-transform: uppercase; 52 | background: #219653 !important; 53 | font-weight: 550 !important; 54 | border: none !important; 55 | } 56 | .table-title{ 57 | color: #03009F; 58 | font-weight: 550; 59 | } 60 | .suspend-button{ 61 | width: 100%; 62 | font-weight: 550 !important; 63 | } 64 | .basic-info{ 65 | padding: 20px; 66 | background: #FFFFFF; 67 | border: none !important; 68 | box-shadow: -1px 10px 19px rgba(3, 0, 159, 0.1); 69 | } 70 | 71 | .basic-info > h4{ 72 | color: #2F80ED; 73 | font-weight: 550; 74 | } 75 | 76 | .info-label{ 77 | color: #4F4F4F; 78 | font-weight: 500; 79 | font-size: 18px; 80 | } 81 | 82 | .s-name{ 83 | color: black; 84 | font-weight: 550; 85 | } 86 | 87 | .selector{ 88 | cursor: pointer !important; 89 | } 90 | 91 | .toast{ 92 | margin: 5px !important; 93 | border-radius: 0px !important; 94 | border: none !important; 95 | position: relative !important; 96 | float: right !important; 97 | font-weight: 550 !important; 98 | width: 200px !important; 99 | font-size: 18px !important; 100 | } 101 | 102 | .errorToast{ 103 | border-left: red 3px solid !important; 104 | margin: auto !important; 105 | float: left !important; 106 | font-size: 14px !important; 107 | } 108 | 109 | .successToast{ 110 | border-left: #32B6A2 3px solid !important; 111 | } 112 | 113 | .upload-wrapper{ 114 | padding: 5px; 115 | border-radius: 50px; 116 | border: none; 117 | background: transparent; 118 | z-index: 1; 119 | position: relative; 120 | top: 100px; 121 | right: 5%; 122 | } 123 | 124 | .img{ 125 | width: 150px !important; 126 | object-fit: cover; 127 | 128 | } 129 | .camera-icon{ 130 | background:white; 131 | width:40px; 132 | height:40px; 133 | border-radius:50%; 134 | display:flex; 135 | justify-content: center; 136 | position:absolute; 137 | bottom:5px; 138 | right:-20px; 139 | z-index: 1; 140 | padding: 10px; 141 | cursor: pointer; 142 | } 143 | 144 | .button{ 145 | border: none !important; 146 | font-weight: 550 !important; 147 | text-transform: uppercase; 148 | width: 80%; 149 | margin-top: auto; 150 | font-size: 14px !important; 151 | } 152 | 153 | .card-button{ 154 | border: none !important; 155 | font-weight: 550 !important; 156 | text-transform: uppercase; 157 | width: auto; 158 | margin-top: 10px; 159 | font-size: 14px !important; 160 | } 161 | 162 | .course-card{ 163 | margin: 5px; 164 | border: none !important; 165 | box-shadow: -2px 5px 18px rgba(3, 0, 159, 0.15); 166 | transition: all cubic-bezier(0.075, 0.82, 0.165, 1) 2s; 167 | } 168 | 169 | 170 | .course-card:hover{ 171 | transform: scale(1.05); 172 | } 173 | 174 | 175 | .navy{ 176 | background-color: #0021CA !important; 177 | } 178 | 179 | .subtitle{ 180 | color: black !important; 181 | font-weight: 500; 182 | } 183 | 184 | 185 | .profile-menu{ 186 | margin-right: 10px !important; 187 | } 188 | 189 | .profile-dropdown{ 190 | color: white !important; 191 | font-family: 'Source Sans Pro'; 192 | font-weight: 550 !important; 193 | background-color: transparent !important; 194 | margin: 0 !important; 195 | border: 1px solid !important; 196 | } 197 | .profile-dropdown:active{ 198 | background-color: transparent !important; 199 | outline: none !important; 200 | } 201 | 202 | .profile-dropdown:focus{ 203 | background-color: transparent !important; 204 | outline: none !important; 205 | } 206 | 207 | /* .navbar{ 208 | background: #AFD1FF !important; 209 | } */ 210 | 211 | .slider-img{ 212 | width:100%; 213 | height:400px; 214 | } 215 | .carousel{ 216 | width:100%; 217 | } 218 | 219 | @media (max-width: 767px) { 220 | .slider-img{ 221 | width:100%; 222 | height:200px; 223 | } 224 | } 225 | 226 | .question{ 227 | color: navy; 228 | font-size: 18px; 229 | font-family: 'Source Sans Pro', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 230 | } 231 | .title{ 232 | font-family: 'Montserrat', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 233 | font-weight: 550; 234 | color: darkblue; 235 | } 236 | .user{ 237 | font-size: 16px; 238 | font-weight:500; 239 | } 240 | 241 | .q-card{ 242 | box-shadow: 1px 4px 12px rgba(3, 0, 159, 0.1); 243 | border: none !important; 244 | } 245 | 246 | .footer{ 247 | text-align: center; 248 | font-size: 16px; 249 | font-family: 'Source Sans Pro'; 250 | margin: auto; 251 | background-color: #AFD1FF; 252 | color: navy; 253 | position: relative; 254 | bottom: 0; 255 | width: 100%; 256 | overflow: hidden; 257 | width: 100%; 258 | } 259 | 260 | .notif-heading{ 261 | background: #03009F !important; 262 | font-family: 'Source Sans Pro'; 263 | font-weight: 550; 264 | text-align: center; 265 | color: white; 266 | border: none; 267 | } 268 | 269 | .add-notif{ 270 | width: 100%; 271 | font-family: 'Source Sans Pro'; 272 | text-transform: uppercase; 273 | font-weight: 550 !important; 274 | } 275 | 276 | .notifications{ 277 | border: none; 278 | box-shadow: 0px 4px 4px rgba(3, 0, 159, 0.2); 279 | } 280 | .link{ 281 | color: white !important; 282 | text-decoration: none !important; 283 | } 284 | .course-title{ 285 | color: #4F4F4F; 286 | } 287 | 288 | .c-title{ 289 | color: black; 290 | font-weight: 550; 291 | } 292 | 293 | .video-title{ 294 | font-size: 20px; 295 | font-weight: 550; 296 | } 297 | 298 | .video-card{ 299 | margin: 0px 5px; 300 | padding: 10px; 301 | } 302 | 303 | .play-button{ 304 | background: navy !important; 305 | } 306 | .view-button{ 307 | background:royalblue !important; 308 | } 309 | 310 | .card-link{ 311 | text-decoration: none; 312 | color: black; 313 | } 314 | 315 | .a-card{ 316 | margin: 5px; 317 | border: none !important; 318 | box-shadow: -2px 5px 18px rgba(3, 0, 159, 0.15); 319 | } 320 | 321 | .a-title{ 322 | color:black; 323 | font-size: 20px; 324 | font-weight: 550; 325 | } 326 | 327 | .edit{ 328 | background:crimson !important; 329 | } 330 | 331 | .a-btn{ 332 | border: none !important; 333 | } 334 | 335 | .view{ 336 | background: #03009F !important; 337 | } 338 | 339 | .a-subtitle{ 340 | font-size: 16px !important; 341 | margin: 10px 0px; 342 | font-weight: 550; 343 | color: darkslategrey !important; 344 | } 345 | 346 | .comment-user{ 347 | color: #2F80ED; 348 | font-weight: 550; 349 | } 350 | .answers{ 351 | color: #03009F; 352 | font-weight: 600; 353 | } 354 | 355 | .comment{ 356 | border-radius: 0% !important; 357 | width: auto; 358 | position: relative; 359 | margin: 10px; 360 | } 361 | 362 | .comment-answer{ 363 | color: black; 364 | font-weight: 600; 365 | } 366 | 367 | .question-card{ 368 | width: auto; 369 | padding: 10px; 370 | height: auto; 371 | } 372 | .comment-section{ 373 | padding: 10px; 374 | background: #F5F5F5; 375 | border: 1px solid lightgray; 376 | width: auto; 377 | height: auto; 378 | } 379 | .commenter{ 380 | color: #03009F; 381 | font-weight: 550; 382 | } 383 | 384 | .comments{ 385 | width: auto; 386 | padding: 10px; 387 | border: 1px solid lightgray; 388 | background-color: white; 389 | } 390 | .user{ 391 | color: crimson; 392 | font-weight: 600; 393 | } 394 | .forum-question{ 395 | color: darkblue; 396 | font-size: 25px; 397 | font-weight: 600; 398 | } 399 | 400 | .topic{ 401 | width: auto; 402 | background-color:transparent !important; 403 | color: #219653 !important; 404 | border: #219653 1px solid; 405 | } 406 | 407 | .remove-question{ 408 | cursor: pointer; 409 | color: red; 410 | font-weight: 550 !important; 411 | font-size: 12px !important; 412 | } 413 | 414 | .empty-div{ 415 | border-radius: 10px; 416 | border: 0.1px solid #dae4ee; 417 | padding: 100px; 418 | width: 100%; 419 | } 420 | .center-text{ 421 | text-align: center; 422 | } 423 | 424 | 425 | .signup-box{ 426 | height: 95vh; 427 | position:relative; 428 | background-size: cover; 429 | background-repeat: no-repeat; 430 | background-image: linear-gradient(0deg, rgba(3, 0, 159, 0.6), rgba(3, 0, 159, 0.6)),url('./Assets/background.png'); 431 | } 432 | 433 | .error{ 434 | color: orangered; 435 | margin: 8px 0px; 436 | font-weight: 550 !important; 437 | font-size: 16px !important; 438 | } 439 | 440 | .logo-container{ 441 | width: 100%; 442 | height: 100px; 443 | overflow: hidden; 444 | z-index: 0; 445 | background-color: #2D9CDB; 446 | position: absolute; 447 | top: 40%; 448 | text-align: center; 449 | } 450 | 451 | .logo{ 452 | margin-top: 10px; 453 | } 454 | 455 | .signup-container{ 456 | font-family: 'Source Sans Pro' !important; 457 | font-size: 18px; 458 | font-weight: 600; 459 | margin-top: 50px; 460 | } 461 | 462 | .signup-button{ 463 | margin: 10px 0px; 464 | font-weight: 600 !important; 465 | width: 20%; 466 | } 467 | 468 | .logo-mobile{ 469 | display: none; 470 | } 471 | 472 | .login-button{ 473 | width: 20%; 474 | } 475 | 476 | .heading{ 477 | font-family: 'Montserrat', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 478 | color: #03009F; 479 | font-style: normal; 480 | font-weight: bold; 481 | } 482 | 483 | .login-helper{ 484 | margin: 10px 0px; 485 | font-size: 16px; 486 | font-weight: 500 !important; 487 | } 488 | 489 | .login-helper > a { 490 | cursor: pointer; 491 | color:#2D9CDB !important; 492 | font-weight: 600; 493 | } 494 | 495 | 496 | 497 | @media (max-width: 767px) { 498 | .signup-box{ 499 | height: auto; 500 | display: flex; 501 | background: none; 502 | } 503 | .signup-container{ 504 | margin-top: 150px; 505 | margin-bottom: 50px; 506 | } 507 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import {Redirect, Route, Switch } from 'react-router-dom'; 2 | import React from 'react' 3 | import './App.css'; 4 | import Login from './Auth/Login'; 5 | import Signup from './Auth/Signup'; 6 | import AdminDashboard from './Dashboards/admin/AdminDashboard' 7 | import StudentDashboard from './Dashboards/student/StudentDashboard' 8 | import TeacherDashboard from './Dashboards/teacher/TeacherDashboard' 9 | import {connect} from 'react-redux'; 10 | import './App.css' 11 | import ErrorPage from './Errorpage'; 12 | import CustomNavbar from './Components/Navbar/Navbar'; 13 | import {ADMIN_ROUTES, STUDENT_ROUTES, TEACHER_ROUTES} from './Routes' 14 | import CustomAlert from './Components/Alert' 15 | 16 | 17 | 18 | 19 | class App extends React.Component{ 20 | render(){ 21 | const {profile,auth} = this.props; 22 | 23 | var links; 24 | 25 | if(profile.userType === "Admin"){ 26 | links = ADMIN_ROUTES; 27 | }else if(profile.userType === "Teacher"){ 28 | links = TEACHER_ROUTES; 29 | }else{ 30 | links = STUDENT_ROUTES; 31 | } 32 | 33 | return( 34 |
35 | {auth && !auth.uid ? '' : } 36 | 37 | 38 | 39 | 40 | { 41 | auth && !auth.uid && 42 | } 43 | { 44 | profile.userType === "Admin" && 45 | } 46 | { 47 | profile.userType === "Student" && 48 | } 49 | { 50 | profile.userType === "Teacher" && 51 | } 52 | 53 |
54 | ) 55 | } 56 | } 57 | 58 | const mapStateToProps = (state) => { 59 | return { 60 | profile: state.firebase.profile, 61 | auth: state.firebase.auth, 62 | } 63 | } 64 | 65 | 66 | export default connect(mapStateToProps)(App); 67 | -------------------------------------------------------------------------------- /src/Assets/Fest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashankbhat2/lms/f40dd5f6c42ac1a87dbf1b73ae5a8cf64c85c09e/src/Assets/Fest.png -------------------------------------------------------------------------------- /src/Assets/Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashankbhat2/lms/f40dd5f6c42ac1a87dbf1b73ae5a8cf64c85c09e/src/Assets/background.png -------------------------------------------------------------------------------- /src/Assets/camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Assets/hack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashankbhat2/lms/f40dd5f6c42ac1a87dbf1b73ae5a8cf64c85c09e/src/Assets/hack.png -------------------------------------------------------------------------------- /src/Auth/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Row, Col, Container, Form, FormGroup, Label, Input, Button} from 'reactstrap' 3 | import '../App.css' 4 | import {Redirect} from 'react-router-dom' 5 | import {ReactComponent as Logo} from '../Assets/Logo.svg' 6 | import { Link } from 'react-router-dom' 7 | import {connect} from 'react-redux'; 8 | import {signIn} from '../Store/actions/authActions'; 9 | import Footer from '../Components/Footer/Footer' 10 | import CustomAlert from '../Components/Alert' 11 | 12 | class Login extends React.Component{ 13 | constructor(props){ 14 | super(props) 15 | 16 | this.state={ 17 | input: { 18 | email: '', 19 | password: '' 20 | }, 21 | errors: {} 22 | } 23 | 24 | } 25 | 26 | handleChange = (e) => { 27 | const input = this.state.input; 28 | const errors = this.state.errors; 29 | input[e.target.id] = e.target.value; 30 | errors[e.target.id] = ''; 31 | this.setState({input}); 32 | this.setState({errors}) 33 | } 34 | 35 | handleSubmit = (e) => { 36 | e.preventDefault(); 37 | if(this.validate()){ 38 | this.props.signIn(this.state.input); 39 | } 40 | } 41 | 42 | 43 | validate(){ 44 | let input = this.state.input; 45 | let errors = {}; 46 | let isValid = true; 47 | 48 | 49 | if(!input["email"]){ 50 | isValid = false; 51 | errors["email"] = "Please enter your email"; 52 | } 53 | 54 | if(!input["password"]){ 55 | isValid = false; 56 | errors["password"] = "Please enter the password"; 57 | } 58 | 59 | this.setState({ 60 | errors: errors 61 | }) 62 | 63 | return isValid; 64 | } 65 | 66 | 67 | render(){ 68 | const {auth, authError} = this.props; 69 | if(auth.uid) return () 70 | 71 | return( 72 | 73 | 74 |
75 |
76 | 77 |
78 |
79 | 80 | 81 |
82 | 83 |

Login

84 | 85 | 86 | 87 | 88 | 89 | {this.state.errors.email &&

{this.state.errors.email}

} 90 | 91 |
92 |
93 | 94 | 95 | 96 | 97 | {this.state.errors.password &&

{this.state.errors.password}

} 98 |
99 |
100 | 101 | 102 |

Dont have an account? Signup

103 | {authError && } 104 |
105 |
106 | 107 | 108 |
109 | ) 110 | } 111 | } 112 | 113 | const mapStateToProps = (state) =>{ 114 | return{ 115 | auth : state.firebase.auth, 116 | authError: state.auth.authError, 117 | } 118 | } 119 | 120 | const mapDispatchToProps=(dispatch)=>{ 121 | return{ 122 | signIn : (creds) => dispatch(signIn(creds)) 123 | } 124 | } 125 | 126 | export default connect(mapStateToProps,mapDispatchToProps)(Login); 127 | -------------------------------------------------------------------------------- /src/Auth/Signup.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Row, Col, Container, Form, FormGroup, Label, Input, Button} from 'reactstrap' 3 | import '../App.css' 4 | import {ReactComponent as Logo} from '../Assets/Logo.svg' 5 | import { Link, Redirect } from 'react-router-dom' 6 | import {connect} from 'react-redux'; 7 | import {signUp} from '../Store/actions/authActions'; 8 | import { compose } from 'redux' 9 | import { firestoreConnect } from 'react-redux-firebase' 10 | import Footer from '../Components/Footer/Footer' 11 | import CustomAlert from '../Components/Alert' 12 | 13 | class Signup extends React.Component{ 14 | constructor(props){ 15 | super(props); 16 | 17 | this.state={ 18 | input:{ 19 | email: '', 20 | name: '', 21 | password: '', 22 | branch: 'CSE', 23 | type: 'Student', 24 | phone: '', 25 | semester: 'First', 26 | }, 27 | errors: {} 28 | } 29 | } 30 | 31 | handleChange = (e) => { 32 | const input = this.state.input 33 | const errors = this.state.errors 34 | input[e.target.name] = e.target.value.trim(); 35 | errors[e.target.name] = ""; 36 | this.setState({input}) 37 | this.setState({errors}) 38 | } 39 | 40 | handleSubmit = (e) => { 41 | e.preventDefault(); 42 | if(this.validate()){ 43 | this.props.signUp(this.state.input); 44 | } 45 | } 46 | 47 | validate = () => { 48 | let input = this.state.input; 49 | let errors = {}; 50 | let isValid = true; 51 | 52 | if(!input["email"]){ 53 | isValid = false; 54 | errors["email"] = "Please enter your email address" 55 | } 56 | 57 | if (typeof input["email"] !== "undefined") { 58 | 59 | var pattern = new RegExp(/^(("[\w-\s]+")|([\w-]+(?:\.[\w-]+)*)|("[\w-\s]+")([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i); 60 | if (!pattern.test(input["email"])) { 61 | isValid = false; 62 | errors["email"] = "Please enter valid email address."; 63 | } 64 | } 65 | 66 | if (!input["password"]) { 67 | isValid = false; 68 | errors["password"] = "Please enter your password."; 69 | } 70 | 71 | if (!input["name"]) { 72 | isValid = false; 73 | errors["name"] = "Please enter your name."; 74 | } 75 | if(!input["phone"]){ 76 | isValid = false; 77 | errors["phone"] = "Please add your phone number" 78 | } 79 | 80 | 81 | this.setState({ 82 | errors: errors 83 | }) 84 | 85 | return isValid; 86 | } 87 | 88 | 89 | 90 | render(){ 91 | const {auth, branches, semesters, authError} = this.props; 92 | if(auth.uid) return () 93 | return( 94 | 95 | 96 |
97 |
98 | 99 |
100 |
101 | 102 | 103 |
104 | 105 |

Signup

106 | 107 | 108 | 109 | 110 | 111 | {this.state.errors.name &&

{this.state.errors.name}

} 112 |
113 | 114 | 115 | 116 | 117 | 118 | {this.state.errors.email &&

{this.state.errors.email}

} 119 |
120 | 121 | 122 | 123 | 124 | 125 | {this.state.errors.password &&

{this.state.errors.password}

} 126 |
127 | 128 | 129 | 130 | 131 | 132 | {this.state.errors.phone &&

{this.state.errors.phone}

} 133 |
134 | 135 | 136 | 137 | 138 | 139 | {branches && branches.map(branch => ( 140 | <> 141 | 142 | 143 | )) 144 | } 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 156 | 157 | 158 | 159 | 160 | 161 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 176 | 177 | 178 | 179 | 180 | 181 | 184 | 185 | 186 | 187 | 188 | {this.state.input.type === "Teacher" ? '' : 189 | 190 | 191 | 192 | {semesters && semesters[0].sems.map(sem => ( 193 | <> 194 | 195 | 196 | )) 197 | } 198 | 199 | 200 | } 201 |
202 | 203 |

Have an account already? Login

204 | {authError && } 205 |
206 |
207 | 208 |