From d321ffea9d9ad513976b54473796e03bcd1660ed Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Tue, 27 Dec 2022 23:46:39 +0200 Subject: [PATCH 01/11] feat: add paying functionality --- BackEnd/controller/course/courseController.js | 654 +++++++++--------- BackEnd/models/lib/payment/paymentSchema.js | 65 +- .../src/Components/Cards/buyCourseCard.js | 384 +++++----- frontend/src/pages/coursePage.js | 102 +-- 4 files changed, 574 insertions(+), 631 deletions(-) diff --git a/BackEnd/controller/course/courseController.js b/BackEnd/controller/course/courseController.js index d2d73ea..ed39efd 100644 --- a/BackEnd/controller/course/courseController.js +++ b/BackEnd/controller/course/courseController.js @@ -1,386 +1,358 @@ -const Course = require('../../models/course/courseSchema') +const Course = require("../../models/course/courseSchema"); -const Fuse = require('fuse.js') -const CourseSection = require('../../models/course/courseSectionSchema') -const CourseSubtitle = require('../../models/course/courseSubtitleSchema') -const Question = require('../../models/course/questionSchema') -const CourseSectionProgress = require('../../models/course/courseProgress/courseSectionProgress') +const Fuse = require("fuse.js"); +const CourseSection = require("../../models/course/courseSectionSchema"); +const CourseSubtitle = require("../../models/course/courseSubtitleSchema"); +const Question = require("../../models/course/questionSchema"); +const CourseSectionProgress = require("../../models/course/courseProgress/courseSectionProgress"); +const Payment = require("../../models/lib/payment/paymentSchema"); +const Trainee = require("../../models/traineeSchema"); const stripe = require("stripe")( - process.env.STRIPE_KEY || - "sk_test_51MFdfGDZGE0sbGNF5ijUysPTPes2023txzn262u9e3xrA8hgD3Sou1KumZqfRU88MG0xz6DLZNRAQWdWM8N8G0Ra00Em82CNAr" - ); - + process.env.STRIPE_KEY || + "sk_test_51MFdfGDZGE0sbGNF5ijUysPTPes2023txzn262u9e3xrA8hgD3Sou1KumZqfRU88MG0xz6DLZNRAQWdWM8N8G0Ra00Em82CNAr" +); //get all courses available const getAllCourses = async (req, res) => { - const courses = await Course.find().sort({ - createdAt: -1 - }) + const courses = await Course.find().sort({ + createdAt: -1, + }); - res.status(200).json(courses) -} + res.status(200).json(courses); +}; //get a course const getCourseById = async (req, res) => { - const _id = req.query + const _id = req.query; - const course = await Course.findById(_id) + const course = await Course.findById(_id); - if (!course) { - return res.status(404).json({ - error: 'course not found' - }) - } + if (!course) { + return res.status(404).json({ + error: "course not found", + }); + } - res.status(200).json(course) -} + res.status(200).json(course); +}; const getCourseByInstructor = async (req, res) => { - - const courses = await Course.find().sort({ - createdAt: -1 - }) - const options = { - includeScore: true, - keys: ['name'] - } - const fuse = new Fuse(courses, (options)) - const result = fuse.search(req.instructor._id) - res.status(200).json(result) -} + const courses = await Course.find().sort({ + createdAt: -1, + }); + const options = { + includeScore: true, + keys: ["name"], + }; + const fuse = new Fuse(courses, options); + const result = fuse.search(req.instructor._id); + res.status(200).json(result); +}; const getCourseByTitle = async (req, res) => { - const courses = await Course.find().sort({ - createdAt: -1 - }) - const options = { - includeScore: true, - keys: ['name'] - } - const fuse = new Fuse(courses, options) - const result = fuse.search(req.title) - res.status(200).json(result) -} + const courses = await Course.find().sort({ + createdAt: -1, + }); + const options = { + includeScore: true, + keys: ["name"], + }; + const fuse = new Fuse(courses, options); + const result = fuse.search(req.title); + res.status(200).json(result); +}; const getCourseBySubject = async (req, res) => { - const courses = await Course.find().sort({ - createdAt: -1 - }) - const options = { - includeScore: true, - keys: ['subject'] - } - const fuse = new Fuse(courses, options) - const result = fuse.search(req.subject) - res.status(200).json(result) -} + const courses = await Course.find().sort({ + createdAt: -1, + }); + const options = { + includeScore: true, + keys: ["subject"], + }; + const fuse = new Fuse(courses, options); + const result = fuse.search(req.subject); + res.status(200).json(result); +}; //get course title and total hours and ratings const getAll = async (req, res) => { - const courses = await Course.find({}, ) -} + const courses = await Course.find({}); +}; const getCoursesByCategory = async (req, res) => { - const { - category - } = req.body - try { - const courses = await Course.getCoursesByCategory(category) - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { category } = req.body; + try { + const courses = await Course.getCoursesByCategory(category); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCoursesByPrice = async (req, res) => { - const { - price - } = req.body - console.log(req.body) - console.log(price) - try { - const courses = await Course.getCoursesByPrice(price) - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { price } = req.body; + console.log(req.body); + console.log(price); + try { + const courses = await Course.getCoursesByPrice(price); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCoursesByPriceFromLowToHigh = async (req, res) => { - try { - const courses = await Course.getCoursesByPriceFromLowToHigh() - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + try { + const courses = await Course.getCoursesByPriceFromLowToHigh(); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const search = async (req, res) => { - const { - searchedword - } = req.body - - - try { - const courses = await Course.search(searchedword) - - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - - - } -} + const { searchedword } = req.body; + + try { + const courses = await Course.search(searchedword); + + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCoursesByPriceFromHighToLow = async (req, res) => { - try { - const courses = await Course.getCoursesByPriceFromHighToLow() - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + try { + const courses = await Course.getCoursesByPriceFromHighToLow(); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCoursesByRating = async (req, res) => { - const { - rating - } = req.body - try { - const courses = await Course.getCoursesByRating(rating) - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { rating } = req.body; + try { + const courses = await Course.getCoursesByRating(rating); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCoursesByRatingFromLowToHigh = async (req, res) => { - try { - const courses = await Course.getCoursesByRatingFromLowToHigh() - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + try { + const courses = await Course.getCoursesByRatingFromLowToHigh(); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCoursesByRatingFromHighToLow = async (req, res) => { - try { - const courses = await Course.getCoursesByRatingFromHighToLow() - res.status(200).json(courses) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + try { + const courses = await Course.getCoursesByRatingFromHighToLow(); + res.status(200).json(courses); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCourseSections = async (req, res) => { - - const { - _id - } = req.query - - try { - if (!_id) - throw Error('All fields must be filled') - - const course = await Course.find({ - _id - }) - if (!course) - throw Error('Course Does not Exist') - - const sections = await CourseSection.find({ - course_id : _id - }) - if (!sections) - throw Error('No Sections Exist in this Course') - - res.status(200).json(sections) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { _id } = req.query; + + try { + if (!_id) throw Error("All fields must be filled"); + + const course = await Course.find({ + _id, + }); + if (!course) throw Error("Course Does not Exist"); + + const sections = await CourseSection.find({ + course_id: _id, + }); + if (!sections) throw Error("No Sections Exist in this Course"); + + res.status(200).json(sections); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getCourseSubtitles = async (req, res) => { - - const { - course_id, - section_id, - } = req.query - - console.log(req.query) - - try { - if ( !course_id || !section_id) - throw Error('All fields must be filled') - - const course = await Course.find({ - course_id - }) - if (!course) - throw Error('Course Does not Exist') - - const sections = await CourseSection.find({ - course_id - }) - if (!sections) - throw Error('No Sections Exist in this Course') - - const subtitles = await CourseSubtitle.find({ - course_id, - section_id - }) - if (!subtitles)//THIS ERROR SHOULD NEVER APPEAR AS WE ALWAYS MAKE THE COURSE AND HANDLE IT IF IT APPEARED D: - throw Error('No Subtitles Exist in this Course') - - res.status(200).json(subtitles) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { course_id, section_id } = req.query; + + console.log(req.query); + + try { + if (!course_id || !section_id) throw Error("All fields must be filled"); + + const course = await Course.find({ + course_id, + }); + if (!course) throw Error("Course Does not Exist"); + + const sections = await CourseSection.find({ + course_id, + }); + if (!sections) throw Error("No Sections Exist in this Course"); + + const subtitles = await CourseSubtitle.find({ + course_id, + section_id, + }); + if (!subtitles) + //THIS ERROR SHOULD NEVER APPEAR AS WE ALWAYS MAKE THE COURSE AND HANDLE IT IF IT APPEARED D: + throw Error("No Subtitles Exist in this Course"); + + res.status(200).json(subtitles); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getQuestion = async (req, res) => { - - const { - _id - } = req.query - - try { - if (!_id) - throw Error('All fields must be filled') - - const question = await Question.findById({ - _id - }) - if (!question) - throw Error('Question Does not Exist') - - res.status(200).json(question) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { _id } = req.query; + + try { + if (!_id) throw Error("All fields must be filled"); + + const question = await Question.findById({ + _id, + }); + if (!question) throw Error("Question Does not Exist"); + + res.status(200).json(question); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const getSectionProgress = async (req, res) => { - - const { - user_id, - course_id, - } = req.query - - try { - if (!user_id || !course_id) - throw Error('All fields must be filled') - - const sectionProgress = await CourseSectionProgress.findOne({ - user_id:user_id, - course_id:course_id, - }) - if (!sectionProgress) - throw Error('Sectoion Progress Does not Exist') - - res.status(200).json(sectionProgress) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { user_id, course_id } = req.query; + + try { + if (!user_id || !course_id) throw Error("All fields must be filled"); + + const sectionProgress = await CourseSectionProgress.findOne({ + user_id: user_id, + course_id: course_id, + }); + if (!sectionProgress) throw Error("Sectoion Progress Does not Exist"); + + res.status(200).json(sectionProgress); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const payForCourse = async (req, res) => { - const { course_id, user_id } = req.body; - - try { - //find course by id - - const course = await Course.findById({ - _id: course_id, - }); - if (!course) throw Error("Course Does not Exist"); - const title = course.title; - const description = course.description; - const price = course.price; - const checkoutSession = await stripe.checkout.sessions.create({ - success_url: `http://localhost:3001/course/coursePage/${course_id}&${user_id}`, - //&session_id={CHECKOUT_SESSION_ID} - cancel_url: "http://localhost:3000/", - client_reference_id: user_id, - line_items: [ - { - quantity: 1, - price_data: { - currency: "usd", - unit_amount: price, - product_data: { - name: title, - description: description, - }, + const { course_id, user_id } = req.body; + + try { + //find course by id + + const course = await Course.findById({ + _id: course_id, + }); + if (!course) throw Error("Course Does not Exist"); + const title = course.title; + const description = course.description; + const price = course.price; + const checkoutSession = await stripe.checkout.sessions.create({ + success_url: `http://localhost:3001/course/coursePage/${course_id}&${user_id}?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `http://localhost:3001/course/coursePage/${course_id}&${user_id}`, + client_reference_id: user_id, + line_items: [ + { + quantity: 1, + price_data: { + currency: "usd", + unit_amount: price, + product_data: { + name: title, + description: description, }, }, - ], - mode: "payment", - }); - - res.status(200).json(checkoutSession.url); - } catch (error) { - res.status(400).json({ - error: error.message, - }); - } - }; - - const saveCheckout = async (req, res) => { - const { session_id, course_id } = req.query; - try { - const session = await stripe.checkout.sessions.retrieve(session_id); - const payment = new Payment({ - user_id: session.client_reference_id, - course_id: course_id, - session_id: session_id, - amount: session.price, - currency: session.currency, - type: session.payment_method_types[0], - }); - res.status(200).json(payment); - } catch (error) { - res.status(400).json({ - error: error.message, - }); - } - }; - - - + }, + ], + mode: "payment", + }); + + res.status(200).json(checkoutSession.url); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; + +const saveCheckout = async (req, res) => { + const { session_id, course_id } = req.body; + try { + const session = await stripe.checkout.sessions.retrieve(session_id); + const user_id = session.client_reference_id; + + //check in payment schema if there exists a payment with the same session_id + const paymentSearch = await Payment.findOne({ + session_id: session_id, + }); + if (paymentSearch) throw Error("Payment Already Exists"); + + const payment = new Payment({ + user_id: session.client_reference_id, + course_id: course_id, + session_id: session_id, + amount: session.price, + currency: session.currency, + type: session.payment_method_types[0], + }); + const trainee = await Trainee.joinCourse(user_id, course_id); + + res.status(200).json({ payment: payment, trainee: trainee }); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; module.exports = { - getAllCourses, - getCourseById, - getCoursesByCategory, - getCoursesByPriceFromLowToHigh, - getCoursesByPriceFromHighToLow, - getCoursesByRating, - getCoursesByRatingFromLowToHigh, - getCoursesByRatingFromHighToLow, - getCoursesByPrice, - getCourseSections, - getCourseSubtitles, - getQuestion, - search, - getSectionProgress, - payForCourse, - saveCheckout, -} \ No newline at end of file + getAllCourses, + getCourseById, + getCoursesByCategory, + getCoursesByPriceFromLowToHigh, + getCoursesByPriceFromHighToLow, + getCoursesByRating, + getCoursesByRatingFromLowToHigh, + getCoursesByRatingFromHighToLow, + getCoursesByPrice, + getCourseSections, + getCourseSubtitles, + getQuestion, + search, + getSectionProgress, + payForCourse, + saveCheckout, +}; diff --git a/BackEnd/models/lib/payment/paymentSchema.js b/BackEnd/models/lib/payment/paymentSchema.js index e403719..f01d53f 100644 --- a/BackEnd/models/lib/payment/paymentSchema.js +++ b/BackEnd/models/lib/payment/paymentSchema.js @@ -1,48 +1,51 @@ -const mongoose = require('mongoose') +const mongoose = require("mongoose"); -const Schema = mongoose.Schema - - -const PaymentSchema = new Schema({ +const Schema = mongoose.Schema; +const PaymentSchema = new Schema( + { course_id: { - type: mongoose.Schema.Types.ObjectId, - ref: 'Course', - required: true + type: mongoose.Schema.Types.ObjectId, + ref: "Course", + required: true, }, user_id: { - type: mongoose.Schema.Types.ObjectId, - ref: 'User', - required: true + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true, + }, + session_id: { + type: String, + required: true, + unique: true, }, price: { - type: Number, - required: true + type: Number, + required: true, }, status: { - type: Boolean, + type: Boolean, }, type: { - type: String, - enum: ['masterCard', 'visa', 'quiz', 'grade'], //todo - required: true + type: String, + enum: ["masterCard", "visa", "quiz", "grade"], //todo + required: true, }, currency: { - type: String, - enum: ['egp', 'usd', 'euro', 'aed'], - required: true + type: String, + enum: ["egp", "usd", "euro", "aed"], + required: true, }, discount: { - discount_id: mongoose.Schema.Types.ObjectId, - percentage: Number, - start_date: Date, - end_data: Date + discount_id: mongoose.Schema.Types.ObjectId, + percentage: Number, + start_date: Date, + end_data: Date, }, + }, + { + timestamps: true, + } +); -}, { - timestamps: true -}) - - - -module.exports = mongoose.model('payment', PaymentSchema) \ No newline at end of file +module.exports = mongoose.model("payment", PaymentSchema); diff --git a/frontend/src/Components/Cards/buyCourseCard.js b/frontend/src/Components/Cards/buyCourseCard.js index 7eb7902..bd263e3 100644 --- a/frontend/src/Components/Cards/buyCourseCard.js +++ b/frontend/src/Components/Cards/buyCourseCard.js @@ -1,213 +1,207 @@ -import React from 'react' -import { useState, useEffect } from 'react'; -import axios from 'axios' -import img1 from "../images/Hotel.png" -import img2 from "../images/Swimming.png" -import img3 from "../images/Wi-Fi.png" -import img4 from "../images/Restaurant.png" -import img5 from "../images/Group.png" -import img6 from "../images/Lesson.png" -import img7 from "../images/Level.png" -import img8 from "../images/Clock.png" -import img9 from "../images/students.png" - -import "../css/buyCourseCard.css" -import { Link } from 'react-router-dom' -export default function BuyCourseCard({course,traineeID,payPage}) { - - - const [trainee,setTrainee] = useState(false); - // const [trainee,setTrainee] = useState(''); - const [payPageLink,setPayPageLink] = useState('') - const [isCorporate, setIsCorprate] = useState(false) - var [isOwenedCourse, setIsOwnedCourse] = useState(false) - var isCorprate = false - //var isOwnCourse = false +import React from "react"; +import { useState, useEffect } from "react"; +import axios from "axios"; +import img1 from "../images/Hotel.png"; +import img2 from "../images/Swimming.png"; +import img3 from "../images/Wi-Fi.png"; +import img4 from "../images/Restaurant.png"; +import img5 from "../images/Group.png"; +import img6 from "../images/Lesson.png"; +import img7 from "../images/Level.png"; +import img8 from "../images/Clock.png"; +import img9 from "../images/students.png"; +import "../css/buyCourseCard.css"; +import { Link } from "react-router-dom"; +export default function BuyCourseCard({ course, traineeID, payFunction }) { + const [trainee, setTrainee] = useState(false); + // const [trainee,setTrainee] = useState(''); + const [payPageLink, setPayPageLink] = useState(""); + const [isCorporate, setIsCorprate] = useState(false); + var [isOwenedCourse, setIsOwnedCourse] = useState(false); + var isCorprate = false; + //var isOwnCourse = false + const getTraineeById = async () => { + const res = await axios + .get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) + .catch((err) => console.log(err)); + const data = await res.data; + console.log(res.data); + //isCorprate = res.data.isCorporate + setIsCorprate(res.data.isCorporate); + console.log("mazen is " + res.data.isCorporate); + //console.log(" isCorporate "+isCorprate) + var ownedCourses = trainee.ownedCourses; + + for (let i = 0; i < res.data.ownedCourses.length; i++) { + if (res.data.ownedCourses[i].course_id == course._id) { + console.log("ana da5alt"); + setIsOwnedCourse(true); + } + } + console.log(" isOwnCourse " + isOwenedCourse); + return data; + }; + // const isTraineeOwnCourse = async () => { + // const trainee = data.getTraineeById; + // console.log(trainee); + // }; + const payCourse = async () => { + const res = await axios + .post("http://localhost:3000/lib/payCourse", { + course_id: course._id, + user_id: traineeID, + }) + .catch((err) => console.log(err)); + window.location = res.data; + }; + + //function to check if url has query param success + const checkUrl = () => { + let search = window.location.search; + let params = new URLSearchParams(search); + const session_id = params.get("session_id"); + if (session_id) { + axios + .post("http://localhost:3000/lib/saveCheckout", { + session_id: session_id, + course_id: "63a0b36c2f1dd3195f076039", + }) + .then((res) => { + setIsOwnedCourse(true); + }); + } + }; - const getTraineeById = async () => { - - const res = await axios.get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) - .catch((err) => console.log(err)); - const data = await res.data; - console.log(res.data) - //isCorprate = res.data.isCorporate - setIsCorprate(res.data.isCorporate) - console.log("mazen is " + res.data.isCorporate) - //console.log(" isCorporate "+isCorprate) - var ownedCourses = trainee.ownedCourses - - for (let i = 0; i < res.data.ownedCourses.length; i++) { - - if(res.data.ownedCourses[i].course_id == course._id) - { - console.log("ana da5alt") - setIsOwnedCourse(true) - //isOwnCourse = true - - } - } - console.log(" isOwnCourse "+isOwenedCourse) - return data; - - }; - // const isTraineeOwnCourse = async () => { - // const trainee = data.getTraineeById; - // console.log(trainee); - // }; - - - useEffect(() =>{ - getTraineeById().then((data) => setTrainee(data)) + useEffect(() => { + checkUrl(); + getTraineeById().then((data) => setTrainee(data)); - if(isCorporate && isOwenedCourse){ - console.log("hide the button") - } - + if (isCorporate && isOwenedCourse) { + console.log("hide the button"); + } + }, []); + //console.log(isCorprate) + //console.log(course._id) + // if(isCorprate){ + // console.log("hide the buttons") + // } - },[]) - //console.log(isCorprate) - //console.log(course._id) + const sendCourseRequest = async () => { + const res = await axios + .post("http://localhost:3000/trainee/requestCourse", { + _id: traineeID, + course_id: course._id, + }) + .catch((err) => console.log(err)); + const data = await res.data; - // if(isCorprate){ - // console.log("hide the buttons") - // } + return data; + }; - const sendCourseRequest = async () => { - const res = await axios.post("http://localhost:3000/trainee/requestCourse",{_id:traineeID,course_id:course._id}) - .catch((err) => console.log(err)); - const data = await res.data; - - return data; - - }; + // console.log(traineeID) + // console.log(course._id) - // console.log(traineeID) - // console.log(course._id) + // console.log(isCorprate) - // console.log(isCorprate) - - const handleCourseRequest=(e)=>{ - e.preventDefault() + const handleCourseRequest = (e) => { + e.preventDefault(); - if(isCorprate == true){ - sendCourseRequest(); - } + if (isCorprate == true) { + sendCourseRequest(); } - - - - // const handlePayCourse=(e)=>{ - // e.preventDefault() - - // payCourse(); - // } - - // console.log(payPageLink) - - + }; return (
-
-
- course photo -
-
- - -
-
- - {course.category} -
-
- icon1 - icon2 - icon3 -
-
-

{course.title} With Chris Bryant

-
-
-
- rating -
-
- rating -
-
- rating -
-
- rating -
-
- rating -
-
-
- -
-
- price -
-
- -
-
- -
-
- level Icon -
-
- -
-
- level Icon -
-
- -
-
- level Icon -
-
- -
-
- level Icon -
-
- -
-
-

- {course.summary} -

- - -
- -
- -
- - -
- {/* */} - - - - - - - {/* // */} -
+
+
+ course photo +
+
+ +
+
+ + {course.category} +
+
+ icon1 + icon2 + icon3 +
+
+

{course.title} With Chris Bryant

+
+
+
+ rating +
+
+ rating +
+
+ rating +
+
+ rating +
+
+ rating +
+
+
+ +
+
+ price +
+
+ +
+
+ +
+
+ level Icon +
+
+ +
+
+ level Icon +
+
+ +
+
+ level Icon +
+
+ +
+
+ level Icon +
+
+ +
+
+

{course.summary}

+
+ +
+ {!isOwenedCourse && ( +
+ +
+ )} +
- ) + ); } diff --git a/frontend/src/pages/coursePage.js b/frontend/src/pages/coursePage.js index b5a1ef4..d9e011d 100644 --- a/frontend/src/pages/coursePage.js +++ b/frontend/src/pages/coursePage.js @@ -1,85 +1,59 @@ -import React from 'react' -import { useState, useEffect } from 'react' -import axios from 'axios' -import { useParams } from 'react-router-dom' -import BuyCourseCard from '../Components/Cards/buyCourseCard' -import Rating from '../Components/Cards/rating' -import RatingsCard from '../Components/Course/ratingsCard' -import Navbar from '../Components/General/Navbar/navbar' -import "../Components/css/coursePage.css" -import InstructorCardBig from '../Components/Instructor/instructorCardBig' -import TraineeNavbar from '../Components/General/Navbar/TraineeNavbar' +import React from "react"; +import { useState, useEffect } from "react"; +import axios from "axios"; +import { useParams } from "react-router-dom"; +import BuyCourseCard from "../Components/Cards/buyCourseCard"; +import Rating from "../Components/Cards/rating"; +import RatingsCard from "../Components/Course/ratingsCard"; +import Navbar from "../Components/General/Navbar/navbar"; +import "../Components/css/coursePage.css"; +import InstructorCardBig from "../Components/Instructor/instructorCardBig"; +import TraineeNavbar from "../Components/General/Navbar/TraineeNavbar"; export const CoursePage = () => { - const { id } = useParams(); const { traineeID } = useParams(); - const [course,setCourse] = useState([]); - const [instructor,setInstructor] = useState([]); - const [payPageLink,setPayPageLink] = useState('') - - const getCourseById = async () => { - const res = await axios.get(`http://localhost:3000/course/?_id=${id}`) - .catch((err) => console.log(err)); - const data = await res.data; - - return data; - - }; + const [course, setCourse] = useState([]); - const payCourse = async () => { - const res = await axios.post("http://localhost:3000/lib/payCourse",{ - course_id: id , user_id: traineeID - }) - .catch((err) => console.log(err)); - const data = await res.data; - - return data; - - }; + const getCourseById = async () => { + const res = await axios + .get(`http://localhost:3000/course/?_id=${id}`) + .catch((err) => console.log(err)); + const data = await res.data; + console.log("here course", data); + return data; + }; + // const getInstructor = async () => { + // const res = await axios.get(`http://localhost:3000/instructor/getInstructor?_id=${course.instructorid}`) + // .catch((err) => console.log(err)); + // const Instdata = await res.data; - useEffect(() =>{ - payCourse().then((data) => setPayPageLink(data)) - },[]) + // return Instdata; - console.log(payPageLink) - const x= payPageLink.data; - // const getInstructor = async () => { - // const res = await axios.get(`http://localhost:3000/instructor/getInstructor?_id=${course.instructorid}`) - // .catch((err) => console.log(err)); - // const Instdata = await res.data; - - // return Instdata; - - // }; + // }; - // useEffect(() =>{ - // getInstructor().then((Instdata) => setInstructor(Instdata)) - - // },[]) + // useEffect(() =>{ + // getInstructor().then((Instdata) => setInstructor(Instdata)) - useEffect(() =>{ - getCourseById().then((data) => setCourse(data)) - },[]) + // },[]) - console.log(course) + useEffect(() => { + getCourseById().then((data) => setCourse(data)); + }, []); return (
- - + +
- +
{/*
*/} -
- -
+
- - ) -} + ); +}; From c927fb3085a868c564658d7791872e31ba1b8cea Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Wed, 28 Dec 2022 00:10:47 +0200 Subject: [PATCH 02/11] fix: save checkout method type --- BackEnd/routes/libRouter.js | 62 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/BackEnd/routes/libRouter.js b/BackEnd/routes/libRouter.js index 00f49a6..9b2dcd5 100644 --- a/BackEnd/routes/libRouter.js +++ b/BackEnd/routes/libRouter.js @@ -1,31 +1,31 @@ -const express = require('express') -const { - getCourseById, - getCourseSections, - getCourseSubtitles, - getQuestion, - getSectionProgress, - saveCheckout, - payForCourse -} = require('../controller/course/courseController') -const { getMaterial } = require('../controller/trainee/traineeCourseController') -const { getIssue } = require('../controller/userController') - -const router = express.Router() - - -router.get('/Course', getCourseById) -router.get('/CourseSections', getCourseSections) -router.get('/CourseSubtitles', getCourseSubtitles) -router.get('/CourseMaterial', getMaterial) -router.get('/Question', getQuestion) -router.get('/Issue', getIssue) - -router.get('/CourseSectionProgress', getSectionProgress) - -router.post('/payCourse', payForCourse) - -router.get('/saveCheckout', saveCheckout) - - -module.exports = router \ No newline at end of file +const express = require("express"); +const { + getCourseById, + getCourseSections, + getCourseSubtitles, + getQuestion, + getSectionProgress, + saveCheckout, + payForCourse, +} = require("../controller/course/courseController"); +const { + getMaterial, +} = require("../controller/trainee/traineeCourseController"); +const { getIssue } = require("../controller/userController"); + +const router = express.Router(); + +router.get("/Course", getCourseById); +router.get("/CourseSections", getCourseSections); +router.get("/CourseSubtitles", getCourseSubtitles); +router.get("/CourseMaterial", getMaterial); +router.get("/Question", getQuestion); +router.get("/Issue", getIssue); + +router.get("/CourseSectionProgress", getSectionProgress); + +router.post("/payCourse", payForCourse); + +router.post("/saveCheckout", saveCheckout); + +module.exports = router; From cee1fdf61204b7e4795f9a8bdb91fa481ed15a1f Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Wed, 28 Dec 2022 13:24:08 +0200 Subject: [PATCH 03/11] feat: improve course flow --- BackEnd/controller/course/courseController.js | 4 +- frontend/src/App.js | 419 +++++++++++------- .../src/Components/Cards/buyCourseCard.js | 81 ++-- frontend/src/Components/Cards/courseCard.js | 107 ++--- frontend/src/Components/LoginComponent.js | 185 ++++---- frontend/src/pages/coursePage.js | 6 +- frontend/src/pages/traineePage.js | 153 +++---- 7 files changed, 488 insertions(+), 467 deletions(-) diff --git a/BackEnd/controller/course/courseController.js b/BackEnd/controller/course/courseController.js index ed39efd..6c17963 100644 --- a/BackEnd/controller/course/courseController.js +++ b/BackEnd/controller/course/courseController.js @@ -281,8 +281,8 @@ const payForCourse = async (req, res) => { const description = course.description; const price = course.price; const checkoutSession = await stripe.checkout.sessions.create({ - success_url: `http://localhost:3001/course/coursePage/${course_id}&${user_id}?session_id={CHECKOUT_SESSION_ID}`, - cancel_url: `http://localhost:3001/course/coursePage/${course_id}&${user_id}`, + success_url: `http://localhost:3001/course/coursePage/${course_id}?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `http://localhost:3001/course/coursePage/${course_id}`, client_reference_id: user_id, line_items: [ { diff --git a/frontend/src/App.js b/frontend/src/App.js index 9f9bb85..fb7613e 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,183 +1,262 @@ -import { BrowserRouter as Router,Routes, Route} from 'react-router-dom'; -import './App.css'; -import React from 'react'; - - -import Signin from './Components/Authentication/Signin' -import AdminSignup from './pages/admin'; -import GuestPage from './pages/guestPage'; -import { TraineePage } from './pages/traineePage'; -import { HomePage } from './pages/homePage'; -import InstructorPage from './pages/instructor'; -import { CourseDashboard } from './pages/courseDashboard'; -import { CoursePage } from './pages/coursePage'; - -import SearchCoursePage from './pages/searchCoursePage'; -import { InstructorEditProfile } from './pages/instructorEditProfile'; -import { TraineeEditProfile } from './pages/TraineeEditProfile'; -import SidebarPage from './pages/sidebarPage'; -import { InstructorAddCourse } from './pages/instructorAddCoursePage'; - -import InstructorAddQuizPage from './pages/instructorAddQuizPage'; - -import VideoPage from './Components/Video Page/videoPage'; -import Navbar from './Components/General/Navbar/navbar'; -import InstructorCard from './Components/Instructor/instructorCard'; -import TemplatePage from './pages/Template/TemplatePage'; - -import SignUpInstructor from './Components/Authentication/signUpInstructor'; -import SignUpTrainee from './Components/Authentication/signUpTrainee'; - -import SignUp from './Components/Authentication/signUpForm'; -import InstructorAddSectionPage from './pages/instructorAddSectionPage'; -import InstructorAddDiscountPage from './pages/instructorAddDiscountPage'; -import CourseCard from './Components/Cards/courseCard'; -import Dropdown2 from './Components/General/Buttons/CategoryChoices'; - - - -import LoginComponent from './Components/LoginComponent'; -import Login from './pages/login'; -import Terms from './pages/terms'; -import QuizPage from './pages/quizPage'; -import InstructorAddSubtitle from './Components/Instructor/instructorAddSubtitle'; -import InstructorCoursePage from './pages/instructorCoursePage'; -import RateCourse from './Components/Cards/rateCourse'; -import RateInstructor from './Components/Cards/rateInstructor'; -import InstructorHomePage from './pages/instructor'; -import InstructorEditBio from './pages/instructorEditBio'; -import InstructorEditEmail from './pages/instructorEditEmail'; -import InstructorEditPassword from './pages/instructorEditPassword'; -import UserPage from './pages/UserPage'; -import InstructorCardBig from './Components/Instructor/instructorCardBig'; - -import SectionPage from './pages/sectionPage'; -import TermsInstructor from './pages/TermsInstructor'; -import TermsTrainee from './pages/TermsTrainee'; -import AllCourses from './pages/AllCourses'; -import Temp from './pages/Temp'; - - - -import SubtitlePage from './pages/subtitlePage'; -import MaterialPage from './pages/materialPage'; -import Drawer from './Components/drawer'; -import Search from './pages/search'; -import QuestionsComponents from './Components/Instructor/questionsComponents'; -import QuestionCard from './Components/Cards/questionCard'; -import QuestionCard2 from './Components/Cards/questionCard2'; -import QuizResultsPage from './pages/quizResultsPage'; -import ViewInstructorReviews from './pages/ViewInstructorReviews'; -import ForgetPassword from './Components/Authentication/forgetPassword'; - -import AdminReportsPage from './pages/adminReportsPage'; -import TraineeOwnedCourses from './pages/traineeOwnedCourses'; -import FaqPage from './pages/faqPage'; -import AdminCourseRequests from './pages/adminCourseRequests'; -import InstructorAddPreviewURL from './Components/Instructor/instructorAddPreviewURL'; -import InstructorAddPreviewPage from './pages/instructorAddPreviewPage'; -import AddDiscount from './Components/Admin/addDiscount'; -import AdminViewCourses from './pages/adminViewCourses'; -import PreviewPage from './pages/previewPage'; -import ResetPasswordPage from './pages/resetPasswordPage'; - - - - +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import "./App.css"; +import React from "react"; + +import Signin from "./Components/Authentication/Signin"; +import AdminSignup from "./pages/admin"; +import GuestPage from "./pages/guestPage"; +import { TraineePage } from "./pages/traineePage"; +import { HomePage } from "./pages/homePage"; +import InstructorPage from "./pages/instructor"; +import { CourseDashboard } from "./pages/courseDashboard"; +import { CoursePage } from "./pages/coursePage"; + +import SearchCoursePage from "./pages/searchCoursePage"; +import { InstructorEditProfile } from "./pages/instructorEditProfile"; +import { TraineeEditProfile } from "./pages/TraineeEditProfile"; +import SidebarPage from "./pages/sidebarPage"; +import { InstructorAddCourse } from "./pages/instructorAddCoursePage"; + +import InstructorAddQuizPage from "./pages/instructorAddQuizPage"; + +import VideoPage from "./Components/Video Page/videoPage"; +import Navbar from "./Components/General/Navbar/navbar"; +import InstructorCard from "./Components/Instructor/instructorCard"; +import TemplatePage from "./pages/Template/TemplatePage"; + +import SignUpInstructor from "./Components/Authentication/signUpInstructor"; +import SignUpTrainee from "./Components/Authentication/signUpTrainee"; + +import SignUp from "./Components/Authentication/signUpForm"; +import InstructorAddSectionPage from "./pages/instructorAddSectionPage"; +import InstructorAddDiscountPage from "./pages/instructorAddDiscountPage"; +import CourseCard from "./Components/Cards/courseCard"; +import Dropdown2 from "./Components/General/Buttons/CategoryChoices"; + +import LoginComponent from "./Components/LoginComponent"; +import Login from "./pages/login"; +import Terms from "./pages/terms"; +import QuizPage from "./pages/quizPage"; +import InstructorAddSubtitle from "./Components/Instructor/instructorAddSubtitle"; +import InstructorCoursePage from "./pages/instructorCoursePage"; +import RateCourse from "./Components/Cards/rateCourse"; +import RateInstructor from "./Components/Cards/rateInstructor"; +import InstructorHomePage from "./pages/instructor"; +import InstructorEditBio from "./pages/instructorEditBio"; +import InstructorEditEmail from "./pages/instructorEditEmail"; +import InstructorEditPassword from "./pages/instructorEditPassword"; +import UserPage from "./pages/UserPage"; +import InstructorCardBig from "./Components/Instructor/instructorCardBig"; + +import SectionPage from "./pages/sectionPage"; +import TermsInstructor from "./pages/TermsInstructor"; +import TermsTrainee from "./pages/TermsTrainee"; +import AllCourses from "./pages/AllCourses"; +import Temp from "./pages/Temp"; + +import SubtitlePage from "./pages/subtitlePage"; +import MaterialPage from "./pages/materialPage"; +import Drawer from "./Components/drawer"; +import Search from "./pages/search"; +import QuestionsComponents from "./Components/Instructor/questionsComponents"; +import QuestionCard from "./Components/Cards/questionCard"; +import QuestionCard2 from "./Components/Cards/questionCard2"; +import QuizResultsPage from "./pages/quizResultsPage"; +import ViewInstructorReviews from "./pages/ViewInstructorReviews"; +import ForgetPassword from "./Components/Authentication/forgetPassword"; + +import AdminReportsPage from "./pages/adminReportsPage"; +import TraineeOwnedCourses from "./pages/traineeOwnedCourses"; +import FaqPage from "./pages/faqPage"; +import AdminCourseRequests from "./pages/adminCourseRequests"; +import InstructorAddPreviewURL from "./Components/Instructor/instructorAddPreviewURL"; +import InstructorAddPreviewPage from "./pages/instructorAddPreviewPage"; +import AddDiscount from "./Components/Admin/addDiscount"; +import AdminViewCourses from "./pages/adminViewCourses"; +import PreviewPage from "./pages/previewPage"; +import ResetPasswordPage from "./pages/resetPasswordPage"; function App() { return ( <> - - - - } /> - } /> - } /> - } /> - } /> - } /> - }/> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - - - - - } /> - } /> - } /> - - - }/> - } /> - - {/* } /> */} - } /> - } /> - }/> - }/> - }/> - - }/> - }/> - }/> - }/> - }/> - - - - - - - {/* }/> + + + } /> + } + /> + } /> + } /> + } + /> + } /> + } + /> + } /> + } /> + } + /> + } /> + } + /> + } /> + } /> + } + /> + } + /> + } + /> + } + /> + } /> + } + /> + } + /> + } /> + } /> + } /> + } /> + } + /> + + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } /> + } + /> + } + /> + } + /> + } /> + + } + /> + + } /> + } /> + } /> + + } /> + } /> + + {/* } /> */} + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } + /> + } + /> + } + /> + + {/* }/> }/> }/> }/> }/> */} - - - - - - - + + - - ) + ); } export default App; diff --git a/frontend/src/Components/Cards/buyCourseCard.js b/frontend/src/Components/Cards/buyCourseCard.js index bd263e3..6bb517b 100644 --- a/frontend/src/Components/Cards/buyCourseCard.js +++ b/frontend/src/Components/Cards/buyCourseCard.js @@ -11,51 +11,46 @@ import img7 from "../images/Level.png"; import img8 from "../images/Clock.png"; import img9 from "../images/students.png"; import "../css/buyCourseCard.css"; -import { Link } from "react-router-dom"; -export default function BuyCourseCard({ course, traineeID, payFunction }) { +import { useParams } from "react-router-dom"; +export default function BuyCourseCard({ course, payFunction }) { const [trainee, setTrainee] = useState(false); - // const [trainee,setTrainee] = useState(''); - const [payPageLink, setPayPageLink] = useState(""); + const { id } = useParams(); const [isCorporate, setIsCorprate] = useState(false); var [isOwenedCourse, setIsOwnedCourse] = useState(false); var isCorprate = false; - //var isOwnCourse = false + const getTraineeById = async () => { - const res = await axios - .get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) - .catch((err) => console.log(err)); - const data = await res.data; - console.log(res.data); - //isCorprate = res.data.isCorporate - setIsCorprate(res.data.isCorporate); - console.log("mazen is " + res.data.isCorporate); - //console.log(" isCorporate "+isCorprate) - var ownedCourses = trainee.ownedCourses; + const traineeID = window.localStorage.getItem("trainee_id"); + if (traineeID) { + const res = await axios + .get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) + .catch((err) => console.log(err)); + const data = await res.data; + setIsCorprate(res.data.isCorporate); - for (let i = 0; i < res.data.ownedCourses.length; i++) { - if (res.data.ownedCourses[i].course_id == course._id) { - console.log("ana da5alt"); - setIsOwnedCourse(true); + for (let i = 0; i < res.data.ownedCourses.length; i++) { + if (res.data.ownedCourses[i].course_id == id) { + setIsOwnedCourse(true); + } } + return data; } - console.log(" isOwnCourse " + isOwenedCourse); - return data; }; - // const isTraineeOwnCourse = async () => { - // const trainee = data.getTraineeById; - // console.log(trainee); - // }; + const payCourse = async () => { - const res = await axios - .post("http://localhost:3000/lib/payCourse", { - course_id: course._id, - user_id: traineeID, - }) - .catch((err) => console.log(err)); - window.location = res.data; + if (window.localStorage.getItem("trainee_id") == null) { + window.location = "/login"; + } else { + const res = await axios + .post("http://localhost:3000/lib/payCourse", { + course_id: id, + user_id: window.localStorage.getItem("trainee_id"), + }) + .catch((err) => console.log(err)); + window.location = res.data; + } }; - //function to check if url has query param success const checkUrl = () => { let search = window.location.search; let params = new URLSearchParams(search); @@ -64,10 +59,15 @@ export default function BuyCourseCard({ course, traineeID, payFunction }) { axios .post("http://localhost:3000/lib/saveCheckout", { session_id: session_id, - course_id: "63a0b36c2f1dd3195f076039", + course_id: id, }) .then((res) => { setIsOwnedCourse(true); + window.history.replaceState( + {}, + document.title, + "/course/coursePage/" + id + ); }); } }; @@ -80,17 +80,11 @@ export default function BuyCourseCard({ course, traineeID, payFunction }) { console.log("hide the button"); } }, []); - //console.log(isCorprate) - //console.log(course._id) - - // if(isCorprate){ - // console.log("hide the buttons") - // } const sendCourseRequest = async () => { const res = await axios .post("http://localhost:3000/trainee/requestCourse", { - _id: traineeID, + _id: window.localStorage.getItem("trainee_id"), course_id: course._id, }) .catch((err) => console.log(err)); @@ -99,11 +93,6 @@ export default function BuyCourseCard({ course, traineeID, payFunction }) { return data; }; - // console.log(traineeID) - // console.log(course._id) - - // console.log(isCorprate) - const handleCourseRequest = (e) => { e.preventDefault(); diff --git a/frontend/src/Components/Cards/courseCard.js b/frontend/src/Components/Cards/courseCard.js index eaceb1d..9b67876 100644 --- a/frontend/src/Components/Cards/courseCard.js +++ b/frontend/src/Components/Cards/courseCard.js @@ -1,66 +1,73 @@ -import React from 'react'; -import { useState , useEffect} from 'react'; -import axios from 'axios'; -import "../css/courseCard.css" -import img1 from "../images/Course Image.png" -import img2 from "../images/Clock Icon.png" -import img3 from "../images/Lesson Icon.png" -import img4 from "../images/Level Icon.png" -import { Link } from 'react-router-dom'; +import React from "react"; +import { useState, useEffect } from "react"; +import axios from "axios"; +import "../css/courseCard.css"; +import img1 from "../images/Course Image.png"; +import img2 from "../images/Clock Icon.png"; +import img3 from "../images/Lesson Icon.png"; +import img4 from "../images/Level Icon.png"; +import { Link } from "react-router-dom"; -export default function CourseCard({course, traineeID}) { - const [instructorName,setInstructorName] = useState(''); +export default function CourseCard({ course }) { + const [instructorName, setInstructorName] = useState(""); const getInstructor = async () => { - const res = await axios.get(`http://localhost:3000/instructor/getInstructor?_id=${course.instructor_id}`) - .catch((err) => console.log(err)); + const res = await axios + .get( + `http://localhost:3000/instructor/getInstructor?_id=${course.instructor_id}` + ) + .catch((err) => console.log(err)); const data = await res.data; - + return data; - }; - useEffect(() =>{ - - getInstructor().then((data) => setInstructorName(data.name)) - - },[]); + useEffect(() => { + getInstructor().then((data) => setInstructorName(data.name)); + }, []); return ( -
-
-
- - Course Img -
+
+
+
+ Course Img +
-
- -

{course.title}

- +
+ +

{course.title}

+ -
-

- {course.summary} -

-
-
- - - -
- +
+ +
+

{course.summary}

+
+
+ + + +
-
- - - - - - -
+
+ + + + +
-
); } diff --git a/frontend/src/Components/LoginComponent.js b/frontend/src/Components/LoginComponent.js index e561f97..a9d941a 100644 --- a/frontend/src/Components/LoginComponent.js +++ b/frontend/src/Components/LoginComponent.js @@ -1,131 +1,108 @@ -import React from 'react' -import "./loginComponent.css" -import login from "./images/user.svg" -import lock from "./images/lock.svg" -import { useState , useEffect} from 'react'; -import axios from 'axios' -import { Link } from 'react-router-dom'; +import React from "react"; +import "./loginComponent.css"; +import login from "./images/user.svg"; +import lock from "./images/lock.svg"; +import { useState, useEffect } from "react"; +import axios from "axios"; +import { Link } from "react-router-dom"; export default function LoginComponent() { - const[password,setPass] =useState('') - const[email,setEmail] =useState('') - const[userID,setUserID] = useState([]) - const[userRole,setUserRole] = useState(''); - - const[userIsInstructor,setUserIsInstructor] = useState(false); - const[userIsAdmin,setUserIsAdmin] = useState(false); - const[userIsTrainee,setUserIsTrainee] = useState(false); + const [password, setPass] = useState(""); + const [email, setEmail] = useState(""); + const [userID, setUserID] = useState([]); + const [userRole, setUserRole] = useState(""); + const [userIsInstructor, setUserIsInstructor] = useState(false); + const [userIsAdmin, setUserIsAdmin] = useState(false); + const [userIsTrainee, setUserIsTrainee] = useState(false); const loginUser = async () => { // console.log("boodaa") const res = await axios .post("http://localhost:3000/login/", { - email:email,password:password + email: email, + password: password, + }) + .then((res) => { + window.localStorage.setItem("trainee_id", res.data.user._id); }) .catch((err) => console.log(err)); const data = await res.data; - console.log(userID) - console.log(userRole) return data; }; - const handleSubmitt=(e)=>{ - e.preventDefault() - console.log("hi") - + const handleSubmitt = (e) => { + e.preventDefault(); loginUser().then((data) => console.log(data)); - } + }; - useEffect(() =>{ - loginUser().then((data) => setUserID(data.user._id)) - loginUser().then((data) => setUserRole(data.role)) + useEffect(() => { + loginUser().then((data) => setUserID(data.user._id)); + loginUser().then((data) => setUserRole(data.role)); }); - const handleSignIn=(e)=>{ - e.preventDefault() + const handleSignIn = (e) => { + e.preventDefault(); - if(userRole == 'instructor'){ - setUserIsInstructor(true) + if (userRole == "instructor") { + setUserIsInstructor(true); + } else if (userRole == "admin") { + setUserIsAdmin(true); + } else { + setUserIsTrainee(true); } - else if(userRole == 'admin'){ - setUserIsAdmin(true) - - } - else{ - setUserIsTrainee(true) - } - - - } + }; return (
-

WELCOME BACK

- -
-
-
-
-
- setEmail(e.target.value)} - value={email} - - className='user' type="email" placeholder=" email" /> - -
-
-
- setPass(e.target.value)} - value={password} - - className='pass' type="password" placeholder=" Password" /> - - +

WELCOME BACK

+ + +
+
+
+
+ setEmail(e.target.value)} + value={email} + className="user" + type="email" + placeholder=" email" + /> +
+
+
+ setPass(e.target.value)} + value={password} + className="pass" + type="password" + placeholder=" Password" + /> +
+
+
+ +
+ +
+
+ + +

DONT HAVE AN ACCOUNT ?

+ + image + image
-
- - -
- -
-
- - - - - -

DONT HAVE AN ACCOUNT ?

- - - image - image -
- - ) + ); } diff --git a/frontend/src/pages/coursePage.js b/frontend/src/pages/coursePage.js index d9e011d..0ec6747 100644 --- a/frontend/src/pages/coursePage.js +++ b/frontend/src/pages/coursePage.js @@ -12,8 +12,6 @@ import TraineeNavbar from "../Components/General/Navbar/TraineeNavbar"; export const CoursePage = () => { const { id } = useParams(); - const { traineeID } = useParams(); - const [course, setCourse] = useState([]); const getCourseById = async () => { @@ -21,6 +19,7 @@ export const CoursePage = () => { .get(`http://localhost:3000/course/?_id=${id}`) .catch((err) => console.log(err)); const data = await res.data; + console.log("here course", data); return data; }; @@ -46,9 +45,8 @@ export const CoursePage = () => { return (
-
- +
{/*
diff --git a/frontend/src/pages/traineePage.js b/frontend/src/pages/traineePage.js index 657808d..690d278 100644 --- a/frontend/src/pages/traineePage.js +++ b/frontend/src/pages/traineePage.js @@ -1,158 +1,129 @@ -import React from 'react'; -import CourseCard from '../Components/Cards/courseCard'; -import TraineeNavbar from "../Components/General/Navbar/TraineeNavbar" +import React from "react"; +import CourseCard from "../Components/Cards/courseCard"; +import TraineeNavbar from "../Components/General/Navbar/TraineeNavbar"; import { BrowserRouter as Router, Switch, Route, Link, - useParams + useParams, } from "react-router-dom"; -import {useState,useEffect} from 'react'; -import axios from 'axios'; +import { useState, useEffect } from "react"; +import axios from "axios"; -import "../Components/css/traineePage.css" -import InstructorCard from '../Components/Instructor/instructorCard' +import "../Components/css/traineePage.css"; +import InstructorCard from "../Components/Instructor/instructorCard"; // import image1 from "../Components/images/image1.svg" // import image2 from "../Components/images/Learn.svg" // import image3 from "../Components/images/sponser.svg" // import image4 from "../Components/images/Mission.svg" -import image5 from "../Components/images/profile1.svg" -import image9 from "../Components/images/profile2.png" -import image10 from "../Components/images/profile3.png" -import image11 from "../Components/images/profile4.svg" -import image6 from "../Components/images/hat.svg" -import image7 from "../Components/images/grad.svg" -import image8 from "../Components/images/star.svg" +import image5 from "../Components/images/profile1.svg"; +import image9 from "../Components/images/profile2.png"; +import image10 from "../Components/images/profile3.png"; +import image11 from "../Components/images/profile4.svg"; +import image6 from "../Components/images/hat.svg"; +import image7 from "../Components/images/grad.svg"; +import image8 from "../Components/images/star.svg"; export const TraineePage = () => { - - const [courses,setCourses] = useState([]); - const {traineeID} = useParams(); - + const [courses, setCourses] = useState([]); + const { traineeID } = useParams(); const getCourses = async () => { - const res = await axios.get("http://localhost:3000/course/getAllCourses") - .catch((err) => console.log(err)); + const res = await axios + .get("http://localhost:3000/course/getAllCourses") + .catch((err) => console.log(err)); const data = await res.data; - + return data; - }; + useEffect(() => { + getCourses().then((data) => setCourses(data)); + }, []); - - useEffect(() =>{ - getCourses().then((data) => setCourses(data)) - - },[]) - - - - - return (
- -
- +
+
- -

Learn Through our Experts

-
-
- image - +
+ image +

19 Courses

25,599 Students

3

Abdullah shoeib

UI/UX Designer

- image - image - - image + image + image + + image
-
- image - +
+ image +

19 Coursesd

25,599 Students

3

Mazen Hejazy

UI/UX Designer

- image - image - - image + image + image + + image
-
- image - +
+ image +

19 Courses

25,599 Students

3

Marwan Ashraf

UI/UX Designer

- image - image - - image + image + image + + image
-
- image - +
+ image +

19 Courses

25,599 Students

3

Ghazouly El-hendy

UI/UX Designer

- image - image - - image + image + image + + image
-
- - - {courses && courses.map((course , i) =>( - i<3 && - - - ))} - - - - - - - - - + {courses && + courses.map((course, i) => i < 3 && )} + + +
- ) -} + ); +}; From bdd647d3592fe165385a6509a95545cd727f915f Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Wed, 28 Dec 2022 14:56:57 +0200 Subject: [PATCH 04/11] feat: add refund functionality --- BackEnd/controller/course/courseController.js | 12 +- .../controller/trainee/traineeController.js | 330 +++++++++--------- BackEnd/models/lib/payment/paymentSchema.js | 10 +- BackEnd/models/traineeSchema.js | 296 ++++++++-------- BackEnd/routes/traineeRouter.js | 60 ++-- .../General/Navbar/TraineeNavbar.js | 241 +++++++++---- frontend/src/Components/css/navbar.css | 99 +++--- frontend/src/Components/css/traineeNavbar.css | 18 + 8 files changed, 607 insertions(+), 459 deletions(-) diff --git a/BackEnd/controller/course/courseController.js b/BackEnd/controller/course/courseController.js index 6c17963..78a22df 100644 --- a/BackEnd/controller/course/courseController.js +++ b/BackEnd/controller/course/courseController.js @@ -313,20 +313,22 @@ const saveCheckout = async (req, res) => { try { const session = await stripe.checkout.sessions.retrieve(session_id); const user_id = session.client_reference_id; - - //check in payment schema if there exists a payment with the same session_id + const course = await Course.findById({ + _id: course_id, + }); const paymentSearch = await Payment.findOne({ session_id: session_id, }); if (paymentSearch) throw Error("Payment Already Exists"); - - const payment = new Payment({ + const payment = await Payment.create({ + payment_title: course.title, user_id: session.client_reference_id, course_id: course_id, session_id: session_id, - amount: session.price, + price: session.amount_total, currency: session.currency, type: session.payment_method_types[0], + status: "paid", }); const trainee = await Trainee.joinCourse(user_id, course_id); diff --git a/BackEnd/controller/trainee/traineeController.js b/BackEnd/controller/trainee/traineeController.js index d32b800..816a5f3 100644 --- a/BackEnd/controller/trainee/traineeController.js +++ b/BackEnd/controller/trainee/traineeController.js @@ -1,175 +1,187 @@ -const Instructor = require('../../models/InstructorSchema') -const Course = require('../../models/course/courseSchema') -const Trainee = require('../../models/traineeSchema') -const CourseSectionProgress = require('../../models/course/courseProgress/courseSectionProgress') +const Instructor = require("../../models/InstructorSchema"); +const Course = require("../../models/course/courseSchema"); +const Trainee = require("../../models/traineeSchema"); +const CourseSectionProgress = require("../../models/course/courseProgress/courseSectionProgress"); +const Payment = require("../../models/lib/payment/paymentSchema"); const getTraineebyId = async (req, res) => { - const _id = req.query + const _id = req.query; - const trainee = await Trainee.findById(_id) + const trainee = await Trainee.findById(_id); - if (!trainee) { - return res.status(404).json({ - error: 'user not found' - }) - } + if (!trainee) { + return res.status(404).json({ + error: "user not found", + }); + } - return res.status(200).json(trainee) -} + return res.status(200).json(trainee); +}; const joinCourse = async (req, res) => { - const { - _id, - course_id, - } = req.body - - try { - const trainee = await Trainee.joinCourse(_id,course_id) - res.status(200).json({ - trainee, - }) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { _id, course_id } = req.body; + + try { + const trainee = await Trainee.joinCourse(_id, course_id); + res.status(200).json({ + trainee, + }); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const rateCourse = async (req, res) => { - const { - user_id, - course_id, - rating - } = req.body - - try { - if(!user_id) - throw Error('userID not Entered') - const course = await Course.rateCourse(user_id,course_id, rating) - - res.status(200).json({ - course, - }) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} + const { user_id, course_id, rating } = req.body; + + try { + if (!user_id) throw Error("userID not Entered"); + const course = await Course.rateCourse(user_id, course_id, rating); + + res.status(200).json({ + course, + }); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; const reviewInstructor = async (req, res) => { - const { - _id, - instructor_id, - type, - reviewString - } = req.body - console.log(req.body) - try { - const review = await Trainee.reviewInstructor(_id,instructor_id, type, reviewString) - - res.status(200).json({ - review, - }) - } catch (error) { - res.status(400).json({ - error: error.message - }) - } -} -const getPreview = async (req,res) => { - const {courseId} = req.body - - try{ - const previewURl = await Course.find({_id:courseId}).select({coursePreviewUrl:1 , _id:0}) - res.status(200).json({ - previewURl - }) - }catch(error){ - console.log(error) - res.status(400).json({ - error: error.message - }) - } -} -const requestRefund = async (req,res) => { - const { - _id, - course_id - } = req.body - - try{ - if(!_id) - throw Error('userId not Entered') - if(!course_id) - throw Error('courseId not Entered') - - const course = await Course.findOne({ - _id: course_id - }) - var trainee = await Trainee.findOne({ - _id - }) - const sectionProgress = await CourseSectionProgress.findOne({ - user_id:_id, - course_id:course_id - }) - if (!course) - throw Error('Course Does not Exist') - if (!trainee) - throw Error('Trainee Does not Exist') - if (!sectionProgress) - throw Error('Section Progress Does not Exist') - const percentage = sectionProgress.finishedPercentage; - - var ownedCourses = trainee.ownedCourses - - for (let i = 0; i < ownedCourses.length; i++) { - console.log(ownedCourses[i].course_id) - if(ownedCourses[i].course_id == course_id) - { - ownedCourses.splice(i, 1); - - } - } - trainee = await Trainee.findByIdAndUpdate({ - _id - },{ - ownedCourses - }) - - await CourseSectionProgress.deleteOne({ - user_id:_id, - course_id:course_id - }) - - if(percentage < 50) + const { _id, instructor_id, type, reviewString } = req.body; + console.log(req.body); + try { + const review = await Trainee.reviewInstructor( + _id, + instructor_id, + type, + reviewString + ); + + res.status(200).json({ + review, + }); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; +const getPreview = async (req, res) => { + const { courseId } = req.body; + + try { + const previewURl = await Course.find({ _id: courseId }).select({ + coursePreviewUrl: 1, + _id: 0, + }); + res.status(200).json({ + previewURl, + }); + } catch (error) { + console.log(error); + res.status(400).json({ + error: error.message, + }); + } +}; +const requestRefund = async (req, res) => { + const { payment_id } = req.body; + + try { + if (!payment_id) throw Error("payment_id not Entered"); + const payment = await Payment.findOne({ + _id: payment_id, + }); + + const course = await Course.findOne({ + _id: payment.course_id, + }); + var trainee = await Trainee.findOne({ + _id: payment.user_id, + }); + const sectionProgress = await CourseSectionProgress.findOne({ + user_id: payment.user_id, + course_id: payment.course_id, + }); + if (!course) throw Error("Course Does not Exist"); + if (!trainee) throw Error("Trainee Does not Exist"); + // if (!sectionProgress) throw Error("Section Progress Does not Exist"); + // const percentage = sectionProgress.finishedPercentage; + + if (false) { + res.status(200).json({ + message: + "Sorry you can't refund this course you Exceeded 50% of the materials", + }); + } else { + let ownedCourses = trainee.ownedCourses; + console.log("before", ownedCourses); + + //go through ownedcourses and remove the course with the course_id + + ownedCourses = ownedCourses.filter( + (course) => course.course_id.toString() != payment.course_id + ); + console.log("after", ownedCourses); + trainee = await Trainee.findByIdAndUpdate( + { + _id: payment.user_id, + }, { - res.status(200).json({ - ownedCourses - }) + credit: trainee.credit + payment.price, + ownedCourses: ownedCourses, } - else{ - res.status(200).json({ - message: "Sorry you can't refund this course you Exceeded 50% of the materials" - }) + ); + paymentUpdate = await Payment.findByIdAndUpdate( + { + _id: payment_id, + }, + { + status: "Refunded", } - - - - }catch(error){ - console.log(error) - res.status(400).json({ - error: error.message - }) + ); + + await CourseSectionProgress.deleteOne({ + user_id: payment.user_id, + course_id: payment.course_id, + }); + res.status(200).json({ + trainee, + paymentUpdate, + }); } -} + } catch (error) { + console.log(error); + res.status(400).json({ + error: error.message, + }); + } +}; + +const getPaymentById = async (req, res) => { + const { _id } = req.query; + try { + const payments = await Payment.find({ user_id: _id }); + res.status(200).json({ + payments, + }); + } catch (error) { + console.log(error); + res.status(400).json({ + error: error.message, + }); + } +}; module.exports = { - joinCourse, - rateCourse, - reviewInstructor, - getTraineebyId, - getPreview, - requestRefund, - -} \ No newline at end of file + joinCourse, + rateCourse, + reviewInstructor, + getTraineebyId, + getPreview, + requestRefund, + getPaymentById, +}; diff --git a/BackEnd/models/lib/payment/paymentSchema.js b/BackEnd/models/lib/payment/paymentSchema.js index f01d53f..4f981ac 100644 --- a/BackEnd/models/lib/payment/paymentSchema.js +++ b/BackEnd/models/lib/payment/paymentSchema.js @@ -19,16 +19,22 @@ const PaymentSchema = new Schema( required: true, unique: true, }, + payment_title: { + type: String, + required: true, + }, price: { type: Number, required: true, }, status: { - type: Boolean, + type: String, + enum: ["pending", "paid", "failed", "refunded"], + required: true, }, type: { type: String, - enum: ["masterCard", "visa", "quiz", "grade"], //todo + enum: ["card", "quiz", "grade"], //todo required: true, }, currency: { diff --git a/BackEnd/models/traineeSchema.js b/BackEnd/models/traineeSchema.js index 6c3d76e..d7f38a2 100644 --- a/BackEnd/models/traineeSchema.js +++ b/BackEnd/models/traineeSchema.js @@ -1,164 +1,184 @@ -const mongoose = require('mongoose') -const Schema = mongoose.Schema -const User = require('../models/UserSchema') -const Instructor = require('../models/InstructorSchema') -const Course = require('../models/course/courseSchema') -const Review = require('../models/lib/reviewSchema') -const CourseProgress = require('./course/courseProgress/courseProgressSchema') - -const TraineeSchema = new Schema({ - +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; +const User = require("../models/UserSchema"); +const Instructor = require("../models/InstructorSchema"); +const Course = require("../models/course/courseSchema"); +const Review = require("../models/lib/reviewSchema"); +const CourseProgress = require("./course/courseProgress/courseProgressSchema"); + +const TraineeSchema = new Schema( + { _id: { - type: mongoose.Schema.Types.ObjectId, - required: true, - ref: 'User', + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: "User", }, name: { - type: String, + type: String, }, isCorporate: { - type: Boolean, - required: true + type: Boolean, + required: true, }, credticard_details: { - type: String, - + type: String, }, - ownedCourses: [{ + ownedCourses: [ + { course_id: mongoose.Schema.Types.ObjectId, - courseTitle:String, //TODO - }], - followedCourses: [{ + courseTitle: String, //TODO + }, + ], + followedCourses: [ + { course_id: String, //TODO - }], + }, + ], totalPoints: { - type: Number, - + type: Number, }, - info: [{ + info: [ + { degree: String, currentJobTitle: String, universityName: String, universityFaculty: String, yearsExperience: String, - company: String - - }], - //lesa fee ba2y -}, { + company: String, + }, + ], + credit: { + type: Number, + default: 0, + }, + }, + { timestamps: true, -}) -TraineeSchema.statics.signup = async function (email, username, password, firstname, lastname, gender) { - - const role = 'trainee' - const user = await User.signup(email, username, password, firstname, lastname, gender,role) - - const _id = user._id; - - - - const trainee = await this.create({ - _id, - isCorporate: false, - name:firstname + lastname, - }) - - - return trainee - -} -TraineeSchema.statics.reviewInstructor = async function (_id, instructor_id, type, reviewString) { - - if (!_id || !instructor_id || !type || !reviewString) - throw Error('All fields must be filled') - - var instructor = await Instructor.findOne({ - instructor_id - }) - if (!instructor) - throw Error('Instructor Does not Exist') - - const review = await Review.create({ - reviewer_id: _id, - reviewed_id:instructor_id, - type : type, - review : reviewString, - }) - - const instructorReview = { - reviewer_id:_id, - review_id: review._id.toString(), - reviewString:reviewString, - + } +); +TraineeSchema.statics.signup = async function ( + email, + username, + password, + firstname, + lastname, + gender +) { + const role = "trainee"; + const user = await User.signup( + email, + username, + password, + firstname, + lastname, + gender, + role + ); + + const _id = user._id; + + const trainee = await this.create({ + _id, + isCorporate: false, + name: firstname + lastname, + }); + + return trainee; +}; +TraineeSchema.statics.reviewInstructor = async function ( + _id, + instructor_id, + type, + reviewString +) { + if (!_id || !instructor_id || !type || !reviewString) + throw Error("All fields must be filled"); + + var instructor = await Instructor.findOne({ + instructor_id, + }); + if (!instructor) throw Error("Instructor Does not Exist"); + + const review = await Review.create({ + reviewer_id: _id, + reviewed_id: instructor_id, + type: type, + review: reviewString, + }); + + const instructorReview = { + reviewer_id: _id, + review_id: review._id.toString(), + reviewString: reviewString, + }; + + instructor = await Instructor.findByIdAndUpdate( + { + _id: instructor_id, + }, + { + $push: { + reviews: instructorReview, + }, } + ); - instructor = await Instructor.findByIdAndUpdate({ - _id: instructor_id - }, { - $push: { - reviews: instructorReview - }, - } - - ) - - return review; -} + return review; +}; //Should only be called after payment is confirmed TraineeSchema.statics.joinCourse = async function (_id, course_id) { - - if (!_id || !course_id) - throw Error('All fields must be filled') - - const user = await User.findOne({ - _id - }) - if (!user) - throw Error('User Does not Exist') - - const course = await Course.findOne({ - course_id - }) - if (!course) - throw Error('Course Does not Exist') - - const courseProgress = await CourseProgress.create({ - course_id, - user_id:_id, - courseTitle:course.title, - username:user.username, - }) - - const courseObj = { - course_id:course_id, - courseTitle:course.title - } - const trainee = await this.findByIdAndUpdate({ - _id - }, { - $push: { - ownedCourses: courseObj - }, - } - - ) - - const enrolledStudent = { - user_id: user._id, - username:user.username, + if (!_id || !course_id) throw Error("All fields must be filled"); + + const user = await User.findOne({ + _id, + }); + if (!user) throw Error("User Does not Exist"); + + const course = await Course.findOne({ + course_id, + }); + if (!course) throw Error("Course Does not Exist"); + + const courseProgress = await CourseProgress.create({ + course_id, + user_id: _id, + courseTitle: course.title, + username: user.username, + }); + + const courseObj = { + course_id: course_id, + courseTitle: course.title, + }; + const trainee = await this.findByIdAndUpdate( + { + _id, + }, + { + $push: { + ownedCourses: courseObj, + }, } - - await Course.findByIdAndUpdate({ - _id:course_id - },{ - $push: { - enrolledStudents: enrolledStudent, - }, - subscriberNumber : ~~course.subscriberNumber + 1 - }) + ); - return trainee; -} + const enrolledStudent = { + user_id: user._id, + username: user.username, + }; + + await Course.findByIdAndUpdate( + { + _id: course_id, + }, + { + $push: { + enrolledStudents: enrolledStudent, + }, + subscriberNumber: ~~course.subscriberNumber + 1, + } + ); + return trainee; +}; -module.exports = mongoose.model('trainee', TraineeSchema) \ No newline at end of file +module.exports = mongoose.model("trainee", TraineeSchema); diff --git a/BackEnd/routes/traineeRouter.js b/BackEnd/routes/traineeRouter.js index 2d384a7..71c1a0d 100644 --- a/BackEnd/routes/traineeRouter.js +++ b/BackEnd/routes/traineeRouter.js @@ -1,47 +1,47 @@ -const express = require('express') -const { requestCourse } = require('../controller/trainee/corporateController') +const express = require("express"); +const { requestCourse } = require("../controller/trainee/corporateController"); const { - rateCourse, - reviewInstructor, - getTraineebyId, - joinCourse, - getPreview, - requestRefund -} = require('../controller/trainee/traineeController') + rateCourse, + reviewInstructor, + getTraineebyId, + joinCourse, + getPreview, + requestRefund, + getPaymentById, +} = require("../controller/trainee/traineeController"); const { - answerQuestion, - getQuestionGrade, - getQuizGrade, - getJoinedCourses -} = require('../controller/trainee/traineeCourseController') -const User = require('../models/userSchema') + answerQuestion, + getQuestionGrade, + getQuizGrade, + getJoinedCourses, +} = require("../controller/trainee/traineeCourseController"); +const User = require("../models/userSchema"); -const router = express.Router() +const router = express.Router(); -router.post('/joinCourse', joinCourse) +router.post("/joinCourse", joinCourse); -router.post('/rateCourse', rateCourse) +router.post("/rateCourse", rateCourse); -router.post('/reviewInstructor', reviewInstructor) +router.post("/reviewInstructor", reviewInstructor); -router.post('/answerQuestion', answerQuestion) +router.post("/answerQuestion", answerQuestion); -router.post('/getPreview', getPreview) +router.post("/getPreview", getPreview); -router.get('/getTrainee',getTraineebyId) +router.get("/getTrainee", getTraineebyId); -router.get('/questionGrade', getQuestionGrade) +router.get("/questionGrade", getQuestionGrade); -router.get('/quizGrade', getQuizGrade) +router.get("/quizGrade", getQuizGrade); -router.get('/myCourses', getJoinedCourses) - -router.post('/requestRefund', requestRefund) +router.get("/myCourses", getJoinedCourses); +router.post("/requestRefund", requestRefund); +router.get("/getPayments", getPaymentById); //ALL ROUTES BELOW HERE ARE FOR CORPORATE TRAINEE ONLY -router.post('/requestCourse', requestCourse) - +router.post("/requestCourse", requestCourse); -module.exports = router \ No newline at end of file +module.exports = router; diff --git a/frontend/src/Components/General/Navbar/TraineeNavbar.js b/frontend/src/Components/General/Navbar/TraineeNavbar.js index 9cf299e..e87154f 100644 --- a/frontend/src/Components/General/Navbar/TraineeNavbar.js +++ b/frontend/src/Components/General/Navbar/TraineeNavbar.js @@ -1,99 +1,190 @@ -import React from 'react' +import React from "react"; // import "../../css/navbar.css" -import "../../css/traineeNavbar.css" -import search from "../../images/search.png" -import img1 from "../../images/Union.png" -import img2 from "../../images/notification.png" -import img3 from "../../images/question-circle.png" -import img4 from "../../images/settings.png" -import img5 from "../../images/Avatar.png" -import img6 from "../../images/arrow.png" -import Dropdown2 from '../Buttons/CategoryChoices' -import { InstructorEditProfile } from '../../../pages/instructorEditProfile' -import NavyButton from '../Buttons/navyButton' -import { useState,useEffect } from 'react' -import CourseCard from '../../Cards/courseCard' -import axios from 'axios' +import { AiFillWallet } from "react-icons/ai"; +import "../../css/traineeNavbar.css"; +import search from "../../images/search.png"; +import img1 from "../../images/Union.png"; +import img2 from "../../images/notification.png"; +import img3 from "../../images/question-circle.png"; +import img4 from "../../images/settings.png"; +import img5 from "../../images/Avatar.png"; +import img6 from "../../images/arrow.png"; +import Dropdown2 from "../Buttons/CategoryChoices"; +import { InstructorEditProfile } from "../../../pages/instructorEditProfile"; +import NavyButton from "../Buttons/navyButton"; +import { useState, useEffect } from "react"; +import CourseCard from "../../Cards/courseCard"; +import axios from "axios"; +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; export default function TraineeNavbar() { - const [searchedword,setSearchedword] = useState('') - const [courses,setCourses] = useState([]) - const courseSearch = async () => { - console.log("boodaa") - const res = await axios.post("http://localhost:3000/course/search" , { - searchedword:searchedword + const [searchedword, setSearchedword] = useState(""); + const [courses, setCourses] = useState([]); + const [trainee, setTrainee] = useState([]); + const [modalIsOpen, setIsOpen] = React.useState(false); + const [transactions, setTransactions] = useState([]); + function openModal() { + setIsOpen(true); + } + + function closeModal() { + setIsOpen(false); + } + const courseSearch = async () => { + const res = await axios + .post("http://localhost:3000/course/search", { + searchedword: searchedword, + }) + .catch((err) => console.log(err)); + const data = await res.data; + + return data; + }; + const getCourses = async () => { + const res = await axios + .get("http://localhost:3000/course/getAllCourses") + .catch((err) => console.log(err)); + const data = await res.data; + + return data; + }; + + const getTraineeById = async () => { + const traineeID = window.localStorage.getItem("trainee_id"); + if (traineeID) { + const res = await axios + .get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) + .then((res) => { + setTrainee(res.data); }) .catch((err) => console.log(err)); - const data = await res.data; - - return data; - - }; - const getCourses = async () => { - console.log("boodaa") - const res = await axios.get("http://localhost:3000/course/getAllCourses") + } + }; + const getPayments = async () => { + const traineeID = window.localStorage.getItem("trainee_id"); + if (traineeID) { + const res = await axios + .get(`http://localhost:3000/trainee/getPayments?_id=${traineeID}`) + .then((res) => { + setTransactions(res.data.payments); + }) .catch((err) => console.log(err)); - const data = await res.data; - - return data; - - }; - useEffect(() =>{ - getCourses().then((data) => setCourses(data)) - - },[]) - useEffect(() =>{ - courseSearch().then((data) => setCourses(data)) - - },[]) - + } + }; + + const refundPayment = async (payment_id) => { + axios + .post(`http://localhost:3000/trainee/requestRefund`, { + payment_id: payment_id, + }) + .then((res) => { + getPayments(); + }) + .catch((err) => console.log(err)); + }; + useEffect(() => { + getCourses(); + getTraineeById(); + getPayments(); + }, []); + useEffect(() => { + courseSearch().then((data) => setCourses(data)); + }, []); return (
-
- +
- +
-

LearnHub

-
- -
- -
- -
-
-
  • - Reports -
  • +
    + +
    - +
    +
    - icon - icon - icon - icon - icon +
    + + + + Wallet + + + You currently have {trainee.credit} EGP in your wallet. +
    Here is a list of your recent transactions: +

    + {transactions?.map((transaction) => ( +
    +

    {transaction?.payment_title}

    +

    + {transaction?.price} {"EGP".toUpperCase()} +

    + +
    + ))} +
    + + + +
    +
    + + icon + + + icon + + + icon + + + icon + + + icon + + + icon + + <>
    +
    - - - -
    - - ) + ); } diff --git a/frontend/src/Components/css/navbar.css b/frontend/src/Components/css/navbar.css index aa63ad2..eccbf47 100644 --- a/frontend/src/Components/css/navbar.css +++ b/frontend/src/Components/css/navbar.css @@ -1,74 +1,73 @@ -.Nav-signedIn{ - background-color: #FAFDFF; - display: flex; - justify-content: space-between; - padding: 10px 0px; - height: 80px; +.Nav-signedIn { + background-color: #fafdff; + display: flex; + justify-content: space-between; + padding: 10px 0px; + height: 80px; } .sidemenu_button { - width: 30px; - border: none; - background-color: transparent; - /* padding: 0px 10px; */ - margin-top: 5px; - margin-left: 20px; - } + width: 30px; + border: none; + background-color: transparent; + /* padding: 0px 10px; */ + margin-top: 5px; + margin-left: 20px; +} -.Nav-title{ - padding: 5px 0px; - margin-left: 20px; +.Nav-title { + padding: 5px 0px; + margin-left: 20px; } -.Nav-search{ - padding: 10px 0px; - margin-left: 200px; +.Nav-search { + padding: 10px 0px; + margin-left: 200px; } -.Nav-search > input{ - border: none; - border-radius: 20px; - width: 200px; - background-color: rgb(224, 222, 222); +.Nav-search > input { + border: none; + border-radius: 20px; + width: 200px; + background-color: rgb(224, 222, 222); } -.Nav-search > input:focus{ - outline: none; +.Nav-search > input:focus { + outline: none; } -.Nav-actions > ul{ - list-style: none; - padding: 0px; +.Nav-actions > ul { + list-style: none; + padding: 0px; } -.Nav-actions > ul > li{ - list-style: none; - display: inline-table; +.Nav-actions > ul > li { + list-style: none; + display: inline-table; } -.Nav-actions > ul > li > a{ +.Nav-actions > ul > li > a { text-decoration: none; font-size: 20px; padding: 20px; color: #253858; } -.Nav-actions > ul > li > a:hover{ - text-decoration: none; - color: #436fba; - } - -.Nav-userInfo{ - padding: 6px 0px; +.Nav-actions > ul > li > a:hover { + text-decoration: none; + color: #436fba; } -.Nav-userInfo > a{ - padding: 0px 10px; +.Nav-userInfo { + display: flex; + flex-direction: row; + align-items: center; + gap: 25px; + margin-right: 20px; +} +.setting_icon { + border-radius: 16px; + top: 15px; + height: 32px; + width: 32px; + background-color: rgba(255, 255, 255, 0.00009999999747378752); } - -.setting_icon{ - border-radius: 16px; - top: 15px; - height: 32px; - width: 32px; - background-color: rgba(255, 255, 255, 0.00009999999747378752); - } \ No newline at end of file diff --git a/frontend/src/Components/css/traineeNavbar.css b/frontend/src/Components/css/traineeNavbar.css index e69de29..3b36d72 100644 --- a/frontend/src/Components/css/traineeNavbar.css +++ b/frontend/src/Components/css/traineeNavbar.css @@ -0,0 +1,18 @@ +.wallet { + display: flex; + flex-direction: row; + align-items: center; + gap: 15px; +} + +.transaction { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 20px; + margin-bottom: 20px; +} + +.transaction-button { + align-items: center; + justify-content: center; +} From 1e86a3f27be56e8f22fc589ae76314dd5beb0140 Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Fri, 30 Dec 2022 14:58:50 +0200 Subject: [PATCH 05/11] fix: course price --- BackEnd/controller/course/courseController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEnd/controller/course/courseController.js b/BackEnd/controller/course/courseController.js index 78a22df..b5688b5 100644 --- a/BackEnd/controller/course/courseController.js +++ b/BackEnd/controller/course/courseController.js @@ -279,7 +279,7 @@ const payForCourse = async (req, res) => { if (!course) throw Error("Course Does not Exist"); const title = course.title; const description = course.description; - const price = course.price; + const price = Number(course.price + "00"); const checkoutSession = await stripe.checkout.sessions.create({ success_url: `http://localhost:3001/course/coursePage/${course_id}?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `http://localhost:3001/course/coursePage/${course_id}`, From 442fd1c99aff5801c30fc19979584c0867b83035 Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Fri, 30 Dec 2022 21:37:54 +0200 Subject: [PATCH 06/11] feat: add wallet functionality to instructor --- BackEnd/controller/course/courseController.js | 18 +- .../controller/trainee/traineeController.js | 33 ++- BackEnd/models/InstructorSchema.js | 232 ++++++++++-------- .../General/Navbar/TraineeNavbar.js | 12 +- .../General/Navbar/instructorNavbar.js | 130 ++++++---- frontend/src/Components/LoginComponent.js | 2 +- 6 files changed, 251 insertions(+), 176 deletions(-) diff --git a/BackEnd/controller/course/courseController.js b/BackEnd/controller/course/courseController.js index b5688b5..4c92be7 100644 --- a/BackEnd/controller/course/courseController.js +++ b/BackEnd/controller/course/courseController.js @@ -7,7 +7,7 @@ const Question = require("../../models/course/questionSchema"); const CourseSectionProgress = require("../../models/course/courseProgress/courseSectionProgress"); const Payment = require("../../models/lib/payment/paymentSchema"); const Trainee = require("../../models/traineeSchema"); - +const Instructor = require("../../models/InstructorSchema"); const stripe = require("stripe")( process.env.STRIPE_KEY || "sk_test_51MFdfGDZGE0sbGNF5ijUysPTPes2023txzn262u9e3xrA8hgD3Sou1KumZqfRU88MG0xz6DLZNRAQWdWM8N8G0Ra00Em82CNAr" @@ -271,8 +271,6 @@ const payForCourse = async (req, res) => { const { course_id, user_id } = req.body; try { - //find course by id - const course = await Course.findById({ _id: course_id, }); @@ -316,16 +314,28 @@ const saveCheckout = async (req, res) => { const course = await Course.findById({ _id: course_id, }); + const instructor = await Instructor.findById({ + _id: course.instructor_id, + }); const paymentSearch = await Payment.findOne({ session_id: session_id, }); + + await Instructor.findByIdAndUpdate( + { + _id: course.instructor_id, + }, + { + credit: instructor.credit + course.price, + } + ); if (paymentSearch) throw Error("Payment Already Exists"); const payment = await Payment.create({ payment_title: course.title, user_id: session.client_reference_id, course_id: course_id, session_id: session_id, - price: session.amount_total, + price: course.price, currency: session.currency, type: session.payment_method_types[0], status: "paid", diff --git a/BackEnd/controller/trainee/traineeController.js b/BackEnd/controller/trainee/traineeController.js index 816a5f3..5ff0a18 100644 --- a/BackEnd/controller/trainee/traineeController.js +++ b/BackEnd/controller/trainee/traineeController.js @@ -32,6 +32,7 @@ const joinCourse = async (req, res) => { }); } }; + const rateCourse = async (req, res) => { const { user_id, course_id, rating } = req.body; @@ -102,36 +103,47 @@ const requestRefund = async (req, res) => { var trainee = await Trainee.findOne({ _id: payment.user_id, }); - const sectionProgress = await CourseSectionProgress.findOne({ + const instructor = await Instructor.findById({ + _id: course.instructor_id, + }); + let sectionProgress = await CourseSectionProgress.findOne({ user_id: payment.user_id, course_id: payment.course_id, }); + if (!sectionProgress) + sectionProgress = await CourseSectionProgress.create({ + user_id: payment.user_id, + course_id: payment.course_id, + sectionTitle: "Introduction", + }); if (!course) throw Error("Course Does not Exist"); if (!trainee) throw Error("Trainee Does not Exist"); - // if (!sectionProgress) throw Error("Section Progress Does not Exist"); - // const percentage = sectionProgress.finishedPercentage; - if (false) { + const percentage = sectionProgress.finishedPercentage; + if (percentage > 50) { res.status(200).json({ message: "Sorry you can't refund this course you Exceeded 50% of the materials", }); } else { let ownedCourses = trainee.ownedCourses; - console.log("before", ownedCourses); - - //go through ownedcourses and remove the course with the course_id - ownedCourses = ownedCourses.filter( (course) => course.course_id.toString() != payment.course_id ); - console.log("after", ownedCourses); + await Instructor.findByIdAndUpdate( + { + _id: course.instructor_id, + }, + { + credit: instructor.credit - course.price, + } + ); trainee = await Trainee.findByIdAndUpdate( { _id: payment.user_id, }, { - credit: trainee.credit + payment.price, + credit: trainee.credit + course.price, ownedCourses: ownedCourses, } ); @@ -151,6 +163,7 @@ const requestRefund = async (req, res) => { res.status(200).json({ trainee, paymentUpdate, + instructor, }); } } catch (error) { diff --git a/BackEnd/models/InstructorSchema.js b/BackEnd/models/InstructorSchema.js index 5b00077..1f13590 100644 --- a/BackEnd/models/InstructorSchema.js +++ b/BackEnd/models/InstructorSchema.js @@ -1,125 +1,149 @@ -const mongoose = require('mongoose') -const Discount = require('./lib/payment/discountSchema') -const User = require('../models/UserSchema') +const mongoose = require("mongoose"); +const Discount = require("./lib/payment/discountSchema"); +const User = require("../models/UserSchema"); -const Schema = mongoose.Schema +const Schema = mongoose.Schema; -const InstructorSchema = new Schema({ +const InstructorSchema = new Schema( + { _id: { - type: mongoose.Schema.Types.ObjectId, - ref: 'User', - required: true + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true, }, name: { - type: String, + type: String, }, biography: { - type: String, + type: String, }, - offered_courses: [{ + offered_courses: [ + { course_id: mongoose.Schema.Types.ObjectId, //TODO - }], - defined_discounts: [{ + }, + ], + defined_discounts: [ + { discount_id: mongoose.Schema.Types.ObjectId, - discountName :String - }], + discountName: String, + }, + ], prefferedSubject: { - type: String, - enum: ['Web Development', 'Intermediate', 'Mathematics', 'Web Design'], + type: String, + enum: ["Web Development", "Intermediate", "Mathematics", "Web Design"], }, - reviews: [{ + reviews: [ + { reviewer_id: mongoose.Schema.Types.ObjectId, review_id: mongoose.Schema.Types.ObjectId, //type: String, - reviewString : String, - }], - issues: [{ + reviewString: String, + }, + ], + issues: [ + { issue_id: mongoose.Schema.Types.ObjectId, status: String, - }], - -}, { - timestamps: true -}) - -InstructorSchema.statics.signup = async function (email, username, password, firstname, lastname, gender) { - - const role = 'instructor' - const user = await User.signup(email, username, password, firstname, lastname, gender,role) - - - const instructor = await this.create({ - _id: user._id, - name:firstname + lastname, - }) - - return instructor - -} - -InstructorSchema.statics.addDiscount = async function (instructor_id, name, percentage, start_date, end_date) { - - const instructor = await this.findOne({ - _id: instructor_id - }) - - if (!instructor) - throw Error('Instructor does not Exist') - - var discount = await Discount.create({ - name, - percentage, - start_date, - end_date - }) - if(!discount) - throw Error('Discount not created') - - var discountObj = { - discount_id : discount._id, - discountName:name + }, + ], + credit: { + type: Number, + default: 0, + }, + }, + { + timestamps: true, + } +); + +InstructorSchema.statics.signup = async function ( + email, + username, + password, + firstname, + lastname, + gender +) { + const role = "instructor"; + const user = await User.signup( + email, + username, + password, + firstname, + lastname, + gender, + role + ); + + const instructor = await this.create({ + _id: user._id, + name: firstname + lastname, + }); + + return instructor; +}; + +InstructorSchema.statics.addDiscount = async function ( + instructor_id, + name, + percentage, + start_date, + end_date +) { + const instructor = await this.findOne({ + _id: instructor_id, + }); + + if (!instructor) throw Error("Instructor does not Exist"); + + var discount = await Discount.create({ + name, + percentage, + start_date, + end_date, + }); + if (!discount) throw Error("Discount not created"); + + var discountObj = { + discount_id: discount._id, + discountName: name, + }; + + await this.findByIdAndUpdate( + { + _id: instructor_id, + }, + { + $push: { + defined_discounts: discountObj, + }, } + ); - await this.findByIdAndUpdate({ - _id: instructor_id - }, { - $push: { - defined_discounts: discountObj - }, - } - ) - - return discount - -} + return discount; +}; //mehtaga had y3mlha ashan feh hagat kteer awy zay el discount abl man apply el discount lazm nt2kd eno aslan bydy el course dah -InstructorSchema.statics.isTeachCourse = async function (instructor_id, course_id) { - - -} -InstructorSchema.statics.changeBiography = async function (_id,newBiography) { - - if ( !_id ||!newBiography ) - throw Error('All fields must be filled') - - const user = await this.findOne({ - _id - }) - - if (!user) - throw Error('Incorrect id / User not Found') - - - return await this.findByIdAndUpdate({ - _id - }, { - biography: newBiography - }) - -} - - - - +InstructorSchema.statics.isTeachCourse = async function ( + instructor_id, + course_id +) {}; +InstructorSchema.statics.changeBiography = async function (_id, newBiography) { + if (!_id || !newBiography) throw Error("All fields must be filled"); + + const user = await this.findOne({ + _id, + }); + + if (!user) throw Error("Incorrect id / User not Found"); + + return await this.findByIdAndUpdate( + { + _id, + }, + { + biography: newBiography, + } + ); +}; -module.exports = mongoose.model('Instructor', InstructorSchema) \ No newline at end of file +module.exports = mongoose.model("Instructor", InstructorSchema); diff --git a/frontend/src/Components/General/Navbar/TraineeNavbar.js b/frontend/src/Components/General/Navbar/TraineeNavbar.js index e87154f..cf380c0 100644 --- a/frontend/src/Components/General/Navbar/TraineeNavbar.js +++ b/frontend/src/Components/General/Navbar/TraineeNavbar.js @@ -51,10 +51,10 @@ export default function TraineeNavbar() { }; const getTraineeById = async () => { - const traineeID = window.localStorage.getItem("trainee_id"); - if (traineeID) { + const user_id = window.localStorage.getItem("user_id"); + if (user_id) { const res = await axios - .get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) + .get(`http://localhost:3000/trainee/getTrainee?_id=${user_id}`) .then((res) => { setTrainee(res.data); }) @@ -62,10 +62,10 @@ export default function TraineeNavbar() { } }; const getPayments = async () => { - const traineeID = window.localStorage.getItem("trainee_id"); - if (traineeID) { + const user_id = window.localStorage.getItem("user_id"); + if (user_id) { const res = await axios - .get(`http://localhost:3000/trainee/getPayments?_id=${traineeID}`) + .get(`http://localhost:3000/trainee/getPayments?_id=${user_id}`) .then((res) => { setTransactions(res.data.payments); }) diff --git a/frontend/src/Components/General/Navbar/instructorNavbar.js b/frontend/src/Components/General/Navbar/instructorNavbar.js index 6696b33..88d483f 100644 --- a/frontend/src/Components/General/Navbar/instructorNavbar.js +++ b/frontend/src/Components/General/Navbar/instructorNavbar.js @@ -1,64 +1,92 @@ -import React from 'react' -import "../../css/navbar.css" -import search from "../../images/search.png" -import img1 from "../../images/Union.png" -import img2 from "../../images/notification.png" -import img3 from "../../images/question-circle.png" -import img4 from "../../images/settings.png" -import img5 from "../../images/Avatar.png" -import img6 from "../../images/arrow.png" -import Dropdown2 from '../Buttons/CategoryChoices' -import { InstructorEditProfile } from '../../../pages/instructorEditProfile' -import NavyButton from '../Buttons/navyButton' +import React, { useEffect, useState } from "react"; +import "../../css/navbar.css"; +import axios from "axios"; +import search from "../../images/search.png"; +import img1 from "../../images/Union.png"; +import img2 from "../../images/notification.png"; +import img3 from "../../images/question-circle.png"; +import img4 from "../../images/settings.png"; +import img5 from "../../images/Avatar.png"; +import img6 from "../../images/arrow.png"; +import Dropdown2 from "../Buttons/CategoryChoices"; +import { InstructorEditProfile } from "../../../pages/instructorEditProfile"; +import NavyButton from "../Buttons/navyButton"; +import { AiFillWallet } from "react-icons/ai"; export default function InstNavbar() { + const [instructor, setInstructor] = useState([]); + const getInstructorById = async () => { + const user_id = window.localStorage.getItem("user_id"); + if (user_id) { + const res = await axios + .get(`http://localhost:3000/instructor/getInstructor?_id=${user_id}`) + .then((res) => { + setInstructor(res.data); + }) + .catch((err) => console.log(err)); + } + }; + useEffect(() => { + getInstructorById(); + }, []); + return (
    +
    + +
    -
    - -
    - -
    -

    LearnHub

    - -
    - -
    - -
    - -
    - +
    +
    +
  • + Add a course +
  • -
    - icon - icon - icon - icon - icon +
  • + Edit Profile +
  • -
    +
  • + Reports +
  • + +
    +
    + + + icon + + + icon + + + icon + + + icon + + + icon + +
    - ) + ); } diff --git a/frontend/src/Components/LoginComponent.js b/frontend/src/Components/LoginComponent.js index a9d941a..8c62858 100644 --- a/frontend/src/Components/LoginComponent.js +++ b/frontend/src/Components/LoginComponent.js @@ -24,7 +24,7 @@ export default function LoginComponent() { password: password, }) .then((res) => { - window.localStorage.setItem("trainee_id", res.data.user._id); + window.localStorage.setItem("user_id", res.data.user._id); }) .catch((err) => console.log(err)); const data = await res.data; From 4d8388dba511457f08e9161e84c3d2d744df5437 Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Fri, 30 Dec 2022 21:48:14 +0200 Subject: [PATCH 07/11] feat: connected section page with backend progress --- frontend/src/pages/sectionPage.js | 85 +++++++++++++++++-------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/frontend/src/pages/sectionPage.js b/frontend/src/pages/sectionPage.js index a2510a9..afb4041 100644 --- a/frontend/src/pages/sectionPage.js +++ b/frontend/src/pages/sectionPage.js @@ -1,45 +1,52 @@ -import React from 'react' -import {useState, useEffect} from 'react' -import { useParams } from 'react-router-dom' -import SectionCard from '../Components/Cards/sectionCard' -import Navbar from '../Components/General/Navbar/navbar' -import axios from 'axios' -import Progress_bar from '../Components/General/ProgressBar' +import React from "react"; +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import SectionCard from "../Components/Cards/sectionCard"; +import Navbar from "../Components/General/Navbar/navbar"; +import axios from "axios"; +import Progress_bar from "../Components/General/ProgressBar"; export default function SectionPage() { - const{courseid} = useParams(); - const [sections,setSections] = useState([]); - - - - const getsectionsbyCourse_id = async () => { - const res = await axios.get(`http://localhost:3000/lib/CourseSections?_id=${courseid}`) - .catch((err) => console.log(err)); - const data = await res.data; - - return data; - - }; - - useEffect(() =>{ - getsectionsbyCourse_id().then((data) => setSections(data)) - -   },[]); - - + const { courseid } = useParams(); + const [sections, setSections] = useState([]); + const [progress, setProgress] = useState(0); + const getsectionsbyCourse_id = async () => { + const res = await axios + .get(`http://localhost:3000/lib/CourseSections?_id=${courseid}`) + .catch((err) => console.log(err)); + const data = await res.data; + + return data; + }; + + useEffect(() => { + getsectionsbyCourse_id().then((data) => setSections(data)); + getCourseSections(); + }, []); + + const getCourseSections = async () => { + const rest = await axios + .get( + `http://localhost:3000/lib//CourseSectionProgress?course_id=${courseid}&user_id=${window.localStorage.getItem( + "user_id" + )}` + ) + .then((res) => { + console.log(res.data); + setProgress(res.data); + }) + .catch((err) => console.log(err)); + }; return (
    - - -

    {courseid}

    - -
    - - {sections && sections.map((section) =>( - -       ))} - -
    + + +

    {courseid}

    + +
    + {sections && + sections.map((section) => )} +
    - ) + ); } From 106f09507a175ca61401c5414a46f14a34c3786c Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Fri, 30 Dec 2022 23:20:08 +0200 Subject: [PATCH 08/11] fix: buy/refund flow --- frontend/src/Components/Cards/buyCourseCard.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/Components/Cards/buyCourseCard.js b/frontend/src/Components/Cards/buyCourseCard.js index 6bb517b..3b18563 100644 --- a/frontend/src/Components/Cards/buyCourseCard.js +++ b/frontend/src/Components/Cards/buyCourseCard.js @@ -20,10 +20,10 @@ export default function BuyCourseCard({ course, payFunction }) { var isCorprate = false; const getTraineeById = async () => { - const traineeID = window.localStorage.getItem("trainee_id"); - if (traineeID) { + const user_id = window.localStorage.getItem("user_id"); + if (user_id) { const res = await axios - .get(`http://localhost:3000/trainee/getTrainee?_id=${traineeID}`) + .get(`http://localhost:3000/trainee/getTrainee?_id=${user_id}`) .catch((err) => console.log(err)); const data = await res.data; setIsCorprate(res.data.isCorporate); @@ -38,13 +38,13 @@ export default function BuyCourseCard({ course, payFunction }) { }; const payCourse = async () => { - if (window.localStorage.getItem("trainee_id") == null) { + if (window.localStorage.getItem("user_id") == null) { window.location = "/login"; } else { const res = await axios .post("http://localhost:3000/lib/payCourse", { course_id: id, - user_id: window.localStorage.getItem("trainee_id"), + user_id: window.localStorage.getItem("user_id"), }) .catch((err) => console.log(err)); window.location = res.data; @@ -84,7 +84,7 @@ export default function BuyCourseCard({ course, payFunction }) { const sendCourseRequest = async () => { const res = await axios .post("http://localhost:3000/trainee/requestCourse", { - _id: window.localStorage.getItem("trainee_id"), + _id: window.localStorage.getItem("user_id"), course_id: course._id, }) .catch((err) => console.log(err)); From 52d1f716e7d3edcf338d2222378f3d243ef3f4db Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Sat, 31 Dec 2022 00:26:36 +0200 Subject: [PATCH 09/11] feat: add course progress --- BackEnd/controller/course/courseController.js | 57 +++++++++ BackEnd/models/course/courseSectionSchema.js | 119 ++++++++++-------- BackEnd/models/traineeSchema.js | 6 + BackEnd/routes/course/courseRouter.js | 67 +++++----- frontend/src/Components/Cards/sectionCard.js | 111 +++++++++++----- frontend/src/pages/coursePage.js | 2 - frontend/src/pages/sectionPage.js | 5 +- 7 files changed, 241 insertions(+), 126 deletions(-) diff --git a/BackEnd/controller/course/courseController.js b/BackEnd/controller/course/courseController.js index 4c92be7..bd3097f 100644 --- a/BackEnd/controller/course/courseController.js +++ b/BackEnd/controller/course/courseController.js @@ -349,6 +349,62 @@ const saveCheckout = async (req, res) => { }); } }; +const updateTraineeProgress = async (req, res) => { + const { user_id, section_id, course_id } = req.body; + try { + const course = await Course.findById({ + _id: course_id, + }); + const courseSectionProgress = await CourseSectionProgress.findOne({ + user_id: user_id, + course_id: course_id, + }); + if (!courseSectionProgress) + await CourseSectionProgress.create({ + user_id: user_id, + course_id: course_id, + sectionTitle: course.title, + }); + + const trainee = await Trainee.findById(user_id); + if (!trainee) throw Error("Trainee Does not Exist"); + //add section id under course id in trainee progress + let progressLength; + const ownedCourses = trainee.ownedCourses.map((course) => { + if (course.course_id == course_id) { + if (course.sectionProgress?.includes(section_id)) + throw Error("section already exists"); + course.sectionProgress.push(section_id); + progressLength = course.sectionProgress.length; + } + return course; + }); + + await Trainee.findByIdAndUpdate( + { + _id: user_id, + }, + { + ownedCourses: ownedCourses, + } + ); + await CourseSectionProgress.findOneAndUpdate( + { + user_id: user_id, + course_id: course_id, + }, + { + finishedPercentage: (progressLength / ~~course.materialNumber) * 100, + } + ); + + return res.status(200).json(trainee); + } catch (error) { + res.status(400).json({ + error: error.message, + }); + } +}; module.exports = { getAllCourses, @@ -367,4 +423,5 @@ module.exports = { getSectionProgress, payForCourse, saveCheckout, + updateTraineeProgress, }; diff --git a/BackEnd/models/course/courseSectionSchema.js b/BackEnd/models/course/courseSectionSchema.js index 217203e..b24f5f2 100644 --- a/BackEnd/models/course/courseSectionSchema.js +++ b/BackEnd/models/course/courseSectionSchema.js @@ -1,19 +1,19 @@ -const mongoose = require('mongoose') -const courseMaterialSchema = require('./courseMaterialSchema') -const courseSubtitleSchema = require('./courseSubtitleSchema') +const mongoose = require("mongoose"); +const courseMaterialSchema = require("./courseMaterialSchema"); +const courseSubtitleSchema = require("./courseSubtitleSchema"); //Rabena ma3 el nas el gaya tktb hena -const Schema = mongoose.Schema +const Schema = mongoose.Schema; - -const CourseSectionSchema = new Schema({ +const CourseSectionSchema = new Schema( + { course_id: { - type: mongoose.Schema.Types.ObjectId, - ref: 'Course', - required: true + type: mongoose.Schema.Types.ObjectId, + ref: "Course", + required: true, }, sectionTitle: { - type: String, + type: String, }, // totalPoints: { // type: Number, //Ghaleban mesh hanstkhdmhom bttl3 3altool mel subtitle 3ala el course table @@ -24,59 +24,72 @@ const CourseSectionSchema = new Schema({ // maxGrade: { // type: Number, // }, - subtitles: [{ - subtitle_id : mongoose.Schema.Types.ObjectId, + subtitles: [ + { + subtitle_id: mongoose.Schema.Types.ObjectId, subtitleTitle: String, maxGrade: Number, totalPoints: Number, totalHours: Number, - }], - previewImage: {//TODO - type: String, + }, + ], + previewImage: { + //TODO + type: String, }, -}, { - timestamps: true -}) + }, + { + timestamps: true, + } +); -CourseSectionSchema.statics.addSection = async function (course_id, sectionTitle,subtitelTitle,subtitlePreviewVideoUrl) { +CourseSectionSchema.statics.addSection = async function ( + course_id, + sectionTitle, + subtitelTitle, + subtitlePreviewVideoUrl +) { + if (!course_id || !sectionTitle || !subtitelTitle || !subtitlePreviewVideoUrl) + throw error("All fields must be filled"); - if (!course_id || !sectionTitle || !subtitelTitle || !subtitlePreviewVideoUrl) - throw error('All fields must be filled') + const course = await this.find({ + _id: course_id, + }); + if (!course) throw Error("Course Does not Exist"); - const course = await this.find({ - _id: course_id - }) - if (!course) - throw Error('Course Does not Exist') - - const section = await this.create({ - course_id, - sectionTitle - }) - const section_id = section._id; + const section = await this.create({ + course_id, + sectionTitle, + }); + const section_id = section._id; - const subtitle = await courseSubtitleSchema.createSubtitle(course_id, section_id, subtitelTitle,subtitlePreviewVideoUrl,'Lecture') + const subtitle = await courseSubtitleSchema.createSubtitle( + course_id, + section_id, + subtitelTitle, + subtitlePreviewVideoUrl, + "Lecture" + ); - const subtitleObj = { - subtitle_id : subtitle._id, - subtitelTitle, - maxGrade: 0, - totalPoints: 0, - totalHours: 0, - } + const subtitleObj = { + subtitle_id: subtitle._id, + subtitelTitle, + maxGrade: 0, + totalPoints: 0, + totalHours: 0, + }; - const sectionObj = await this.findByIdAndUpdate({ - _id: section_id - }, { - $push: { - subtitles: subtitleObj - }, + const sectionObj = await this.findByIdAndUpdate( + { + _id: section_id, + }, + { + $push: { + subtitles: subtitleObj, + }, } - ) - return sectionObj; - -} - - + ); + return sectionObj; +}; -module.exports = mongoose.model('course Section', CourseSectionSchema) \ No newline at end of file +module.exports = mongoose.model("course Section", CourseSectionSchema); diff --git a/BackEnd/models/traineeSchema.js b/BackEnd/models/traineeSchema.js index d7f38a2..36d65ae 100644 --- a/BackEnd/models/traineeSchema.js +++ b/BackEnd/models/traineeSchema.js @@ -27,8 +27,14 @@ const TraineeSchema = new Schema( { course_id: mongoose.Schema.Types.ObjectId, courseTitle: String, //TODO + sectionProgress: [ + { + section_id: mongoose.Schema.Types.ObjectId, + }, + ], }, ], + followedCourses: [ { course_id: String, //TODO diff --git a/BackEnd/routes/course/courseRouter.js b/BackEnd/routes/course/courseRouter.js index e9da8c5..ac611d2 100644 --- a/BackEnd/routes/course/courseRouter.js +++ b/BackEnd/routes/course/courseRouter.js @@ -1,46 +1,39 @@ -const express = require('express') -const course = require('../../models/course/courseSchema') -const CourseStudyRouter = require("./courseStudyRouter") +const express = require("express"); +const course = require("../../models/course/courseSchema"); +const CourseStudyRouter = require("./courseStudyRouter"); const { - getAllCourses, - getCourseById, - getCoursesByCategory, - getCoursesByPrice, - getCoursesByRating, - getCoursesByPriceFromLowToHigh, - getCoursesByPriceFromHighToLow, - getCourseSections, - getCourseSubtitles, - getQuestion, - search -} = require('../../controller/course/courseController'); - + getAllCourses, + getCourseById, + getCoursesByCategory, + getCoursesByPrice, + getCoursesByRating, + getCoursesByPriceFromLowToHigh, + getCoursesByPriceFromHighToLow, + getCourseSections, + getCourseSubtitles, + getQuestion, + updateTraineeProgress, + search, +} = require("../../controller/course/courseController"); + +const { rateCourse } = require("../../controller/trainee/traineeController"); const { - rateCourse -} = require('../../controller/trainee/traineeController'); -const { getQuestionGrade } = require('../../controller/trainee/traineeCourseController'); - - - + getQuestionGrade, +} = require("../../controller/trainee/traineeCourseController"); const router = express.Router(); +router.use("/courseStudy", CourseStudyRouter); -router.use("/courseStudy", CourseStudyRouter) - - - -router.get('/', getCourseById) -router.get('/getAllCourses', getAllCourses) - - -router.post('/getCourseByCategory', getCoursesByCategory) -router.post('/getCoursesByPrice', getCoursesByPrice) -router.post('/getCoursesByRating', getCoursesByRating) -router.post('/search', search) - -router.post('/course/rateCourse', rateCourse) +router.get("/", getCourseById); +router.get("/getAllCourses", getAllCourses); +router.post("/getCourseByCategory", getCoursesByCategory); +router.post("/getCoursesByPrice", getCoursesByPrice); +router.post("/getCoursesByRating", getCoursesByRating); +router.post("/search", search); +router.post("/course/rateCourse", rateCourse); +router.post("/updateTraineeProgress", updateTraineeProgress); -module.exports = router \ No newline at end of file +module.exports = router; diff --git a/frontend/src/Components/Cards/sectionCard.js b/frontend/src/Components/Cards/sectionCard.js index 3726286..84edcb1 100644 --- a/frontend/src/Components/Cards/sectionCard.js +++ b/frontend/src/Components/Cards/sectionCard.js @@ -1,39 +1,88 @@ -import React from 'react' -import { useState , useEffect } from 'react' -import axios from 'axios' -import img1 from "../images/Course Image.png" -import img2 from "../images/Clock Icon.png" -import img3 from "../images/Lesson Icon.png" -import img4 from "../images/Level Icon.png" -import "../css/sectionCard.css" -import { Link } from 'react-router-dom' +import React from "react"; +import { useState, useEffect } from "react"; +import axios from "axios"; +import img1 from "../images/Course Image.png"; +import img2 from "../images/Clock Icon.png"; +import img3 from "../images/Lesson Icon.png"; +import img4 from "../images/Level Icon.png"; +import "../css/sectionCard.css"; +import { Link } from "react-router-dom"; -export default function SectionCard({section}) { +export default function SectionCard({ section }) { + const [isDone, setIsDone] = useState(false); + const updateProgress = async () => { + const user_id = window.localStorage.getItem("user_id"); + if (user_id) { + await axios + .post("http://localhost:3000/course/updateTraineeProgress", { + user_id: user_id, + section_id: section._id, + course_id: section.course_id, + }) + .then((res) => { + window.location.reload(); + }) + .catch((err) => console.log(err)); + } + }; + const getTraineeById = async () => { + const user_id = window.localStorage.getItem("user_id"); + if (user_id) { + await axios + .get(`http://localhost:3000/trainee/getTrainee?_id=${user_id}`) + .then((res) => { + for (let i = 0; i < res.data.ownedCourses.length; i++) { + if (res.data.ownedCourses[i].course_id === section.course_id) { + for ( + let j = 0; + j < res.data.ownedCourses[i].sectionProgress?.length; + j++ + ) { + if ( + res.data.ownedCourses[i].sectionProgress[j]._id === + section._id + ) { + setIsDone(true); + } + } + } + } + }) + .catch((err) => console.log(err)); + } + }; + + useEffect(() => { + getTraineeById(); + }); return (
    -
    - -
    - section Img -
    - -
    -

    {section.sectionTitle}

    - - -

    - - - -

    -
    - -
    +
    +
    + section Img +
    +
    +

    {section.sectionTitle}

    -
    -
    +

    + + + +

    +
    + +
    +
    - ) + ); } diff --git a/frontend/src/pages/coursePage.js b/frontend/src/pages/coursePage.js index 0ec6747..86d01a0 100644 --- a/frontend/src/pages/coursePage.js +++ b/frontend/src/pages/coursePage.js @@ -19,8 +19,6 @@ export const CoursePage = () => { .get(`http://localhost:3000/course/?_id=${id}`) .catch((err) => console.log(err)); const data = await res.data; - - console.log("here course", data); return data; }; diff --git a/frontend/src/pages/sectionPage.js b/frontend/src/pages/sectionPage.js index afb4041..840f5c7 100644 --- a/frontend/src/pages/sectionPage.js +++ b/frontend/src/pages/sectionPage.js @@ -24,15 +24,14 @@ export default function SectionPage() { }, []); const getCourseSections = async () => { - const rest = await axios + await axios .get( `http://localhost:3000/lib//CourseSectionProgress?course_id=${courseid}&user_id=${window.localStorage.getItem( "user_id" )}` ) .then((res) => { - console.log(res.data); - setProgress(res.data); + setProgress(res.data.finishedPercentage); }) .catch((err) => console.log(err)); }; From 0a42e947a6adbb62e246865d5c860dc81ed2c71a Mon Sep 17 00:00:00 2001 From: Salma Sherif Date: Sat, 31 Dec 2022 18:26:47 +0200 Subject: [PATCH 10/11] feat: rewrite login --- frontend/src/Components/LoginComponent.js | 102 +++++++++------------- 1 file changed, 39 insertions(+), 63 deletions(-) diff --git a/frontend/src/Components/LoginComponent.js b/frontend/src/Components/LoginComponent.js index 632bfc9..77518cb 100644 --- a/frontend/src/Components/LoginComponent.js +++ b/frontend/src/Components/LoginComponent.js @@ -9,12 +9,6 @@ import { Link } from "react-router-dom"; export default function LoginComponent() { const [password, setPass] = useState(""); const [email, setEmail] = useState(""); - const [userID, setUserID] = useState([]); - const [userRole, setUserRole] = useState(""); - - const [userIsInstructor, setUserIsInstructor] = useState(false); - const [userIsAdmin, setUserIsAdmin] = useState(false); - const [userIsTrainee, setUserIsTrainee] = useState(false); const loginUser = async () => { // console.log("boodaa") @@ -25,78 +19,60 @@ export default function LoginComponent() { }) .then((res) => { window.localStorage.setItem("user_id", res.data.user._id); - setUserID(res.data.user._id); + if (res.data.role === "instructor") { + window.location.href = `/instructor/${res.data.user._id}`; + } else if (res.data.role === "admin") { + window.location.href = `/admin/${res.data.user._id}`; + } else { + window.location.href = `/trainee/${res.data.user._id}`; + } }) .catch((err) => console.log(err)); const data = await res.data; return data; }; - const handleSubmitt = (e) => { - e.preventDefault(); - - loginUser().then((data) => console.log(data)); - }; - - useEffect(() => { - loginUser().then((data) => setUserID(data.user._id)); - loginUser().then((data) => setUserRole(data.role)); - }); const handleSignIn = (e) => { e.preventDefault(); - - if (userRole == "instructor") { - setUserIsInstructor(true); - } else if (userRole == "admin") { - setUserIsAdmin(true); - } else { - setUserIsTrainee(true); - } + loginUser(); }; return (

    WELCOME BACK

    -
    -
    -
    -
    -
    - setEmail(e.target.value)} - value={email} - className="user" - type="email" - placeholder=" email" - /> -
    -
    -
    - setPass(e.target.value)} - value={password} - className="pass" - type="password" - placeholder=" Password" - /> -
    - -
    -
    - -
    - -
    +
    +
    +
    + setEmail(e.target.value)} + value={email} + className="user" + type="email" + placeholder=" email" + />
    - +
    +
    + setPass(e.target.value)} + value={password} + className="pass" + type="password" + placeholder=" Password" + /> +
    + +
    +
    + +
    + +

    DONT HAVE AN ACCOUNT ?

    From cb799102a812137eeccdc6d9fe71078b48a90d3c Mon Sep 17 00:00:00 2001 From: Mazen317 Date: Sat, 31 Dec 2022 20:14:53 +0200 Subject: [PATCH 11/11] Msh bgeeb el traineeID mn el local storage 3shan msh bybawaz 7gat 3mlenha --- frontend/src/Components/Cards/sectionCard.js | 4 ++-- frontend/src/pages/materialPage.js | 1 - frontend/src/pages/sectionPage.js | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/Components/Cards/sectionCard.js b/frontend/src/Components/Cards/sectionCard.js index 941c59f..67e55df 100644 --- a/frontend/src/Components/Cards/sectionCard.js +++ b/frontend/src/Components/Cards/sectionCard.js @@ -8,7 +8,7 @@ import img4 from "../images/Level Icon.png"; import "../css/sectionCard.css"; import { Link } from "react-router-dom"; -export default function SectionCard({ section }) { +export default function SectionCard({ section , traineeID }) { const [isDone, setIsDone] = useState(false); const updateProgress = async () => { const user_id = window.localStorage.getItem("user_id"); @@ -67,7 +67,7 @@ export default function SectionCard({ section }) {

    {section.sectionTitle}

    diff --git a/frontend/src/pages/materialPage.js b/frontend/src/pages/materialPage.js index bc1d2e9..8af15b7 100644 --- a/frontend/src/pages/materialPage.js +++ b/frontend/src/pages/materialPage.js @@ -36,7 +36,6 @@ const [quizes,setQuizes] = useState([]); {quizes && quizes.map((quiz) =>( - //

    {quiz._id}

    ))}
    diff --git a/frontend/src/pages/sectionPage.js b/frontend/src/pages/sectionPage.js index 582c0ad..b2230c3 100644 --- a/frontend/src/pages/sectionPage.js +++ b/frontend/src/pages/sectionPage.js @@ -26,8 +26,7 @@ export default function SectionPage() { const getCourseSections = async () => { await axios .get( - `http://localhost:3000/lib//CourseSectionProgress?course_id=${courseid}&user_id=${window.localStorage.getItem( - "user_id" + `http://localhost:3000/lib//CourseSectionProgress?course_id=${courseid}&user_id=${traineeID} )}` ) .then((res) => { @@ -61,7 +60,7 @@ export default function SectionPage() {
    {sections && - sections.map((section) => )} + sections.map((section) => )}