/Akwya

Primary LanguageJavaScriptApache License 2.0Apache-2.0

Akwya

Akwya is an Online Learning System that works with professional instructors to offer online courses, through which individuals can attend pre-recorded courses online. These courses provide quizzes for the user to test his/her understanding of the course; and upon completion, certifications shall be acquired.

Motivation💪

  • This Website was created as a group project for the CSEN704 Course (Advanced Computer Lab).
  • After the COVID-19 pandemic that started in late 2019, learning methods have been shifted worldwide to online methodologies. Since then, online courses have been more convenient to most students so the demand for websites like Akwya has been on the rise.
  • Shaping the ultimate online learning experience was a very fruitful challenge for us.
  • The purpose of creating such project was to practice
    • The MERN (MongoDB , ExpressJS , ReactJS, NodeJS) Technology Stack.
    • Agile software development
    • Up-to-date backend and frontend development techniques

Build Status🛠

The current build of the project contains no bugs, errors or malfunctions, except for the following:

  • Logging in after resetting password requires hashing the password
  • Some UI elements need to look more consistent

Future releases will contain additional features and unit tests.

Code Style✍🏻

  • We used MVC (Model View Controller). For more info visit https://en.wikipedia.org/wiki/Model–view–controller
  • Standard coding style is used to ensure our project's readability and maintainability to developers at any level.
  • Local variables were declared using the standard naming convention, camel case.
  • Prettier extension in Visual Studio Code was used in important files to organize spacing and readability.

Used By👥

This system is used by the following:

  • Admins: Administrators who run the website and are in control of everything.
  • Instructors: Highly qualified instructors bring a wealth of knowledge and experience to our courses and provide students with exceptional material.
  • Individual Trainees: Regular registered user.
  • Corporate Trainees: User who is part of partnered company.
  • Guests: Unregistered user who can only preview features without using them.

Screenshots

Screenshot 2023-01-02 at 4 12 22 AM

Screen Shot 2023-01-06 at 9 51 27 PM

Screenshot 2023-01-02 at 4 07 31 AM

Screen Shot 2023-01-06 at 9 58 16 PM

Screenshot 2023-01-02 at 4 12 04 AM

More Screenshots Screenshot 2023-01-02 at 4 10 13 AM Screenshot 2023-01-02 at 4 11 58 AM Screenshot 2023-01-02 at 4 11 06 AM Screenshot 2023-01-02 at 4 07 53 AM Screenshot 2023-01-02 at 4 10 51 AM Screenshot 2023-01-02 at 4 10 27 AM Screenshot 2023-01-02 at 4 10 23 AM Screenshot 2023-01-02 at 4 07 57 AM Screenshot 2023-01-02 at 4 09 28 AM Screenshot 2023-01-02 at 4 09 53 AM Screenshot 2023-01-02 at 4 09 19 AM Screenshot 2023-01-02 at 4 09 09 AM Screenshot 2023-01-02 at 4 08 29 AM Screenshot 2023-01-02 at 4 12 47 AM Screenshot 2023-01-02 at 4 08 16 AM

Tech Stack

Client:  alt text  alt text  alt text

Server:  alt text  alt text  alt text JSON Web Tokens Badge

Version Control:  alt text  alt text

Features

  • Different Access levels:

    • Admin:

      • Add new users (other Admins, Corporate Trainees, Instructors) to the System
      • Responsible for promotions
      • Grant access to courses
      • Resolve Problems
      • Responsible for financial issues
    • Instructors:

      • Manage their course content
      • Edit their profile
      • Create Quizzes for their courses
      • Add promotions to their courses
      • View their own and their courses' ratings and reviews
      • View their monthly profit
      • Report Problems
    • Trainees:

      • Corporate trainees and individual trainees have similarities in the following features:
        • Explore popular courses
        • Register for courses
        • Take Quiz for the Course
        • View his/her progress in the course
        • Rate the courses and the instructors
        • Take notes while attending the online lecture
        • Report Problems
      • Differences in the following features:
        • Individual trainees are responsible for their payments using the website's wallet system or credit card
        • Individual trainees can request refunds and drop the course
        • Corporate trainees can request access to courses
    • Guest:
      Users with limited functionalities where they can view features but not fully make use of them - Explore popular courses

    • Users:

      • All users have the following features available:
        • Search for courses
        • Filter out courses
  • Fullscreen mode

  • Email Handler

    • Sends Certificate upon Course Completion
    • Used in Recovery of Forgotten Password
  • Well Planned Information Architecture

  • Well-Formatted Content That Is Easy to Scan

    • Visibility
    • Consistent Layout and Formatting
  • Fast Load Times

    • Instant Feedback
    • Fast & Responsive
  • Browser Consistency

  • Effective Navigation

  • Good Error Handling

Usage/Examples

Find below code snippets for how an admin can add a trainee to the system


import mongoose from 'mongoose'; //to connect with mongodb

const traineeSchema = mongoose.Schema({
    username: String,
    password: String,
    email: String,
    fname: String,
    lname: String,
    gender: String,
    traineetype: String, //corporate or individual
    courses: [{ courseid: String, progress: Number,courseName:String }],
    country: String,
    wallet: Number
}, { timestamps: true }
);

const trainee = mongoose.model('trainee', traineeSchema);

export default trainee;
import trainee from "../models/trainee.js"

export const createTrainee= async(req,res) => {
    const {username,password,email}=req.body
    const wallet=0;
    const type = 'corporate'
    //bykon corporate
    try {
            const salt = await bcrypt.genSalt();
            const hashedPassword = await bcrypt.hash(password, salt);
            const newTrainee = await trainee.create({username:username,password:hashedPassword,email:email,traineetype:type,wallet:wallet});
            const token = createToken(username);
    
            res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 });
            res.status(200).json(newTrainee)
      
    } catch (error) {
        res.status(400).json({error: error.message})
    }
}

import express from "express";
const router =express.Router()
import {createTrainee} from "../controllers/adminController.js"

router.post('/newTrainee',createTrainee)
app.use('/admin',adminRoutes);

import traineeRoutes from './routes/trainee.js';

//connect to db
const PORT = process.env.PORT || 8000;

mongoose.connect(process.env.MONG_URI, { useNewURLParser: true, useUnifiedTopology: true })
    .then(() => app.listen(PORT, () => console.log(`Server running on port: ${PORT}`)))
    .catch((error) => console.log(error.message));
import { useState } from 'react'
import Swal from "sweetalert2";
import './admin.css'

const AddTrainee = () => {

    const [username, setUsername] = useState('')
    const [password, setPassword] = useState('')
    const [email, setEmail] = useState('')

    const [error, setError] = useState(null)

    const handleSubmit = async (e) => {
    e.preventDefault()

    const trainee = {username, password,email}
    
# Example of using an API endpoint

    const respnse= await fetch('/admin/newTrainee', {
        method: 'POST',
        body: JSON.stringify(trainee) ,
        headers: {
            'Content-Type' : 'application/json'
        }
    })

    const json= await respnse.json()

    if(!respnse.ok){
        setError(json.error)
    }
    if(respnse.ok){
        console.log("new Trainee added")
        Swal.fire({
            title: 'New Trainee added!',
            icon: 'success',
            confirmButtonColor: '#38a53e',
            confirmButtonText: 'OK'
          })  
        setError(null)
        setUsername('')
        setPassword('')
        setEmail('')
    } 
}

#Example of rendering a React component 
  return (
    <div class="ganb">
    <form className="create" onSubmit={handleSubmit}> 
      <h1>New Trainee</h1>
      <div class="txt_field">
      <input 
        type="username" 
        onChange={(e) => setUsername(e.target.value)} 
        value={username}
        required
      />
       <label>Username</label>
       </div>
       <div class="txt_field">
      <input 
        type="email" 
        onChange={(e) => setEmail(e.target.value)} 
        value={email}
      required/>
      <label>Email</label>
      </div>
      <div class="txt_field">
      <input 
        type="password" 
        onChange={(e) => setPassword(e.target.value)} 
        value={password}
      required/>
      <label>Password</label>
      </div>   

      <button>Add New Trainee</button>
      {error && <div className="error">{error}</div>}
    </form>
    </div>
)
}

export default AddTrainee

Installation

  cd server
  npm Install
  npm install @stripe/react-stripe-js 
  npm install @stripe/stripe-jest 
  npm install @testing-library/jest-dom 
  npm install @testing-library/react 
  npm install @testing-library/user-event axios bootstrap express react react-bootstrap react-dom react-paypal-button-v2 react-router-dom react-scripts react-smart-payment-buttons stripe
  npm install jsonwebtoken
  npm install bcrypt
  npm install cookie-parser
  npm install dotenv
  npm install sweetalert
  npm install sweetalert2 

  cd client
  npm Install
  npm install @stripe/react-stripe-js 
  npm install @stripe/stripe-jest 
  npm install @testing-library/jest-dom 
  npm install @testing-library/react 
  npm install @testing-library/user-event axios bootstrap express react react-bootstrap react-dom react-paypal-button-v2 react-router-dom react-scripts react-smart-payment-buttons stripe
  npm install styled-components

Environment Variables

To run this project, you will need to add the following environment variables to your .env file

  • PORT=Port number to be used
  • MONG_URI= MONG_URI
  • AUTH_EMAIL= Mongo DB Cluster email
  • AUTH_PASS= Mongo DB Cluster password
  • token= Authentication token

How to Use

Clone the project

  git clone https://github.com/Advanced-Computer-Lab-2022/Akwya.git

Go to the project directory

  cd Akwya
  
Install dependencies
```refer to installation section

```Split Terminal

Start the server for the back end
        cd server
        npm start
Start the client for the front end
        cd client
        npm start

API Reference

Admin adding a new Trainee to the system

  POST admin/newTrainee/

Request body

Parameter Type Description
username string trainee's username
password string trainee's initially assigned password
email string trainee's email

Example http://localhost:PORT/admin/newTrainee/

Request { "username":"Farah", "password":"Password123", "email":"farah@hotmail.com" }
Response
{
    "username": "Farah",
    "password": "$2b$10$wBkOfHhfB/bzaCWSZekwo.bfSvy5AtR5kM3AcVZvnMZLV6jj9CdfK",
    "email": "farah@hotmail.com",
    "traineetype": "corporate",
    "wallet": 0,
    "_id": "63b86ec2d563ccbfe98c485d",
    "courses": [],
    "createdAt": "2023-01-06T18:56:02.476Z",
    "updatedAt": "2023-01-06T18:56:02.476Z",
    "__v": 0
}

Viewing a trainee's wallet

  GET /trainee/getWallet/:id

Example http://localhost:PORT/trainee/getWallet/6396424da56263086dde2489

Response
{
    "_id": "6396424da56263086dde2489",
    "wallet": 24377
}

Adding a rating & review to a course

  PATCH /trainee/:id/rateCourse

Example http://localhost:PORT/trainee/63822dd272c5cd10a8568cfb/rateCourse?rating=4&review="My favorite!"

Response { "acknowledged": true, "modifiedCount": 1, "upsertedId": null, "upsertedCount": 0, "matchedCount": 1 }

Viewing an instructor's email

  GET /instructor/viewEmail/:id

Example http://localhost:PORT/instructor/viewEmail/6380fb3a0e91fe67a1baf48c

Response
[
    {
        "_id": "6380fb3a0e91fe67a1baf48c",
        "email": "ayten@hotmail.com"
    }
]

Index APIs

app.use('/user', userRoutes); app.use('/course', courseRoutes); app.use('/admin',adminRoutes); app.use('/instructor', instructorRoutes); app.use('/trainee', traineeRoutes); app.use('/Quiz', quizRoutes);

Admin APIs

router.get('/',getAdmins) router.get('/refundTrainee',refundTrainee) router.post('/newAdmin',createAdmin) router.post('/newInstructor',createInstructor) router.post('/newTrainee',createTrainee) router.get('/courseDiscountAdmin/:id',courseDiscountAdmin) router.get('/promotionFound/:id', promotionFound) router.get('/GrantAccess/:TraineeID/:CourseID', grantAccess) router.get('/RequestAccess/:TraineeID/:CourseID', requestAccess) router.get('/viewRequests/', viewRequests) router.get('/viewRefunds/', viewRefunds)

Course APIs

router.get('/viewCourseDeets',viewCourses) router.get('/viewCoursePrices',viewCoursesPrices) router.get('/filterCoursesOnSubjAndRating/:id/:title',filterCoursesOnSubjAndRating) router.get('/filterCoursesByPrice', filterCoursesByPrice) router.get('/', getCourses) router.get('/search/:search',searchCourse) router.get('/viewACourse/:titlee',viewACourse) router.post('/report',reportAProblem) router.post('/followUp',followUpOnAProblem) router.post('/followUp2',adminFollowUpOnAProblem) router.post('/problemState',problemState) router.post('/:id',createCourse) router.get('/getProblems/:id',getProblems) router.get('/getAllProblems',getAllProblems) router.get('/getMyCourseName/:id',getMyCourseName) router.delete('/:id',deleteCourse) router.delete('/',deleteAllCourses) router.patch('/:id',(req,res)=>{ res.json({mssg:'update a guest'}) }) router.get('/courseDiscount/:id',courseDiscount)

Instructor APIs

router.get('/viewCoursestitleI/:id', viewCoursestitleI ) router.get('/filterMyCoursesByPrice/:id',filterCoursesByPriceI) router.get('/filterMyCoursesBySubject/:id',filterCoursesBySubjectI) router.get('/search/:id/:search',searchCourseI) router.get('/filterCoursesByRatingAndSubject',filterCoursesByRatingAndSubject) router.post('/:id', createCourse ) router.delete('/', deleteAllInstructors ) router.post('/addVideo/:courseID', addVideo ) router.post('/addPreview/:courseID', addPreview ) router.get('/viewVideos/:courseID', viewVideos ) router.get('/viewPreview/:courseID', viewPreview ) router.get('/CanViewVideos/:courseID/:instructorID', CanViewVideos ) router.get('/viewEmail/:id', viewEmail ) router.get('/editEmail/:id', editEmail ) router.get('/viewRating/:id', ViewRating ) router.get('/changePassword/:id', changePassword) router.get('/checkPassword/:id', checkPassword) router.get('/viewBio/:id', viewBio ) router.get('/editBio/:id', editBio ) router.get('/getRatings/:id', getRatings ) router.get('/:id/myRating', ViewRating) router.get('/notFirst/:id', notFirst) router.get('/moneyOwed/:id', moneyOwed)

Quiz APIs

router.post('/create',createQuiz); router.get('/TakeQuiz/:id',getQuiz) router.post('/TakeQuiz/submitQuiz',submitQuiz) router.post('/TakeQuiz/resetQuiz/:id',resetQuiz) router.get('/TakeQuiz/getMyCourseName/:id',getMyCourseName) router.get('/TakeQuiz/viewGrade/:id/:level',viewGrade) router.get('/TakeQuiz/viewQuestionGrade/:id/:quiz',viewQuestionGrade)

Trainee API

router.patch('/:id/rateCourse',rateCourse) router.patch('/:id/rateInstructor',rateInstructor) router.get('/',getTrainee) router.get('/register/:courseID/:traineeID',registerCourse) router.get('/drop/:courseID/:traineeID',dropCourse) router.get('/isRegistered/:courseID/:traineeID',isRegistered) router.get('/changePassword/:id',changePassword) router.get('/checkPassword/:id',checkPassword) router.get('/resetPassword/', resetPassword) router.get('/getWallet/:id', getWallet) router.get('/videoCount/:CourseID', videoCount) router.get('/sendCertificate/:TraineeID/:CourseID', sendCertificate) router.post('/signup', signUp); router.post('/login', login); router.get('/logout', logout); router.post('/stripe', generatePayment); router.post('/confirm-payment', confirmPayment); router.get('/userWatchVideo/:TraineeID/:VideoID', userWatchVideo); router.get('/getUserProgress/:TraineeID/:CourseID', getUserProgress); router.get('/refund/:TraineeID/:CourseID', refundCourse); router.get('/requestRefund/:TraineeID/:CourseID', requestRefund); router.get('/myCourses/:TraineeID', myCourses); router.get('/registerCourseWallet/:courseID/:traineeID',registerCourseWallet);

User API

router.get('/',getAllUser) router.get('/:id',getUser) router.post('/',createUser) router.delete('/:id',deleteUser) router.patch('/:id',updateUser)

Stripe APIs

app.post('/confirm-payment') app.post('/stripe')

Testing with Postman

  • Open Postman and create a new workspace
  • Set your HTTP request (GET/POST/..)
  • In the request URL field, input link
  • Click Send You will see 200 OK Message The response will be shown below according to your requested link

Examples

Screen Shot 2023-01-06 at 9 13 52 PM

Screen Shot 2023-01-06 at 9 28 39 PM

Screen Shot 2023-01-06 at 9 29 39 PM

Screen Shot 2023-01-06 at 9 35 05 PM

Helpful Resources

Useful Resources that were used in the development of this project:

Stripe API:

https://youtu.be/1r-F3FIONl8

Node.js

https://www.youtube.com/playlist?list=PLZlA0Gpn_vH_uZs4vJMIhcinABSTUH2bY

Express.js

https://www.youtube.com/watch?v=fgTGADljAeg

ES6:

https://www.youtube.com/playlist?list=PLZlA0Gpn_vH-0FlQnruw2rd1HuiYJHHkm

React introduction:

https://www.youtube.com/playlist?list=PLZlA0Gpn_vH_NT5zPVp18nGe_W9LqBDQK

React Hooks -- functional components

https://www.youtube.com/playlist?list=PLZlA0Gpn_vH8EtggFGERCwMY5u5hOjf-h

https://youtu.be/hQAHSlTtcmY

JWT authentication:

https://www.youtube.com/watch?v=mbsmsi7l3r4

https://www.youtube.com/watch?v=-RCnNyD0L-s

https://dev.to/salarc123/mern-stack-authentication-tutorial-part-1-the-backend-1c57

License

Copyright (c) 2023 Advanced-Computer-Lab-2022/Akwya

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Contributing🤝

Contributions are always welcome!

Please adhere to this project's code of conduct.

Support

For support, email akwyaawy@hotmail.com