diff --git a/src/app.js b/src/app.js index 7364722..d61665d 100644 --- a/src/app.js +++ b/src/app.js @@ -39,8 +39,6 @@ app.use(morgan("dev")); app.use(mongoSanitize()); - - //Regular middleware app.use(cookieParser()); const csrf = require("csurf"); @@ -72,7 +70,55 @@ app.use("/courses", limiter, isAuthenticated, async function (req, res) { return res.render("course", { courses: courses }); }); +app.post("/search-course", limiter, isAuthenticated, async function (req, res) { + const query = req.body.query; + const regexQuery = { + title: { $regex: query, $options: "i" }, + }; + try { + const searchCourses = await courseModel.findOne(regexQuery); + res.json(searchCourses); + } catch (err) { + console.error(err); + res.json({ message: "An error occurred while searching." }); + } +}); + +app.get("/create-course", csrfProtection, limiter, isAuthenticated, async function (req, res) { + return res.render("course-create", { messageError: req.flash("error"), messageSuccess: req.flash("success"), csrfToken: req.csrfToken() }); +}); +app.post("/create-course", limiter, isAuthenticated, csrfProtection, async function (req, res) { + // TODO: Need to implement upload image logic + try { + const { courseName, shortDescription, longDescription, duration, durationType, imageFile, difficulty } = req.body; + const userName = req.user.fullName; + const findExistingCourse = await courseModel.findOne({ title: {'$regex': `^${courseName}$`, $options: 'i'} }); + if (!findExistingCourse) { + const newCourse = new courseModel({ + title: courseName, + shortDescription: shortDescription, + longDescription: { longDescription: longDescription }, + duration: duration, + durationType: durationType?.toLowerCase(), + difficulty: difficulty, + image: imageFile, + author: userName + }); + + await newCourse.save(); + req.flash("success", "Course created successfully"); + return res.redirect("/create-course"); + } else { + req.flash("error", "This course is already available."); + return res.redirect("/create-course"); + } + } catch (error) { + console.error("Error during course creation:", error); + req.flash("error", "Failed to create the course. Please try again."); + res.redirect("/create-course"); + } +}); app.use("/css", express.static("src/css")); diff --git a/src/css/course-create.css b/src/css/course-create.css new file mode 100644 index 0000000..ef89a32 --- /dev/null +++ b/src/css/course-create.css @@ -0,0 +1,258 @@ +body { + background-color: #ffffff; + color: #444444; + font-family: "Roboto", sans-serif; + font-size: 16px; + font-weight: 300; + margin: 0; + padding: 0; +} +.alert { + padding: 10px; + margin-bottom: 20px; + border-radius: 4px; + position: absolute; + top: 0; + right: 0; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border: 1px solid #f5c6cb; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border: 1px solid #c3e6cb; +} + +.wizard-content-left { + background-blend-mode: darken; + background-color: rgba(0, 0, 0, 0.45); + background-image: url("https://i.ibb.co/X292hJF/form-wizard-bg-2.jpg"); + background-position: center center; + background-size: cover; + height: 100vh; + padding: 30px; +} +.wizard-content-left h1 { + color: #ffffff; + font-size: 38px; + font-weight: 600; + padding: 12px 20px; + text-align: center; +} + +.form-wizard { + color: #888888; + padding: 30px; +} +.form-wizard .wizard-form-radio { + display: inline-block; + margin-left: 5px; + position: relative; +} +.form-wizard .wizard-form-radio input[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; + background-color: #dddddd; + height: 25px; + width: 25px; + display: inline-block; + vertical-align: middle; + border-radius: 50%; + position: relative; + cursor: pointer; +} +.form-wizard .wizard-form-radio input[type="radio"]:focus { + outline: 0; +} +.form-wizard .wizard-form-radio input[type="radio"]:checked { + background-color: #fb1647; +} +.form-wizard .wizard-form-radio input[type="radio"]:checked::before { + content: ""; + position: absolute; + width: 10px; + height: 10px; + display: inline-block; + background-color: #ffffff; + border-radius: 50%; + left: 1px; + right: 0; + margin: 0 auto; + top: 8px; +} +.form-wizard .wizard-form-radio input[type="radio"]:checked::after { + content: ""; + display: inline-block; + webkit-animation: click-radio-wave 0.65s; + -moz-animation: click-radio-wave 0.65s; + animation: click-radio-wave 0.65s; + background: #000000; + content: ""; + display: block; + position: relative; + z-index: 100; + border-radius: 50%; +} +.form-wizard .wizard-form-radio input[type="radio"] ~ label { + padding-left: 10px; + cursor: pointer; +} +.form-wizard .form-wizard-header { + text-align: center; +} +.form-wizard .form-wizard-next-btn, +.form-wizard .form-wizard-previous-btn, +.form-wizard .form-wizard-submit { + background-color: #d65470; + color: #ffffff; + display: inline-block; + min-width: 100px; + min-width: 120px; + padding: 10px; + text-align: center; +} +.form-wizard .form-wizard-next-btn:hover, +.form-wizard .form-wizard-next-btn:focus, +.form-wizard .form-wizard-previous-btn:hover, +.form-wizard .form-wizard-previous-btn:focus, +.form-wizard .form-wizard-submit:hover, +.form-wizard .form-wizard-submit:focus { + color: #ffffff; + opacity: 0.6; + text-decoration: none; +} +.form-wizard .wizard-fieldset { + display: none; +} +.form-wizard .wizard-fieldset.show { + display: block; +} +.form-wizard .wizard-form-error { + display: none; + background-color: #d70b0b; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 2px; + width: 100%; +} +.form-wizard .form-wizard-previous-btn { + background-color: #fb1647; +} +.form-wizard .form-control { + font-weight: 300; + height: auto !important; + padding: 15px; + color: #888888; + background-color: #f1f1f1; + border: none; +} +.form-wizard .form-control:focus { + box-shadow: none; +} +.form-wizard .form-group { + position: relative; + margin: 25px 0; +} +.form-wizard .wizard-form-text-label { + position: absolute; + left: 10px; + top: 16px; + transition: 0.2s linear all; +} +.form-wizard .focus-input .wizard-form-text-label { + color: #d65470; + top: -18px; + transition: 0.2s linear all; + font-size: 12px; +} +.form-wizard .form-wizard-steps { + margin: 30px 0; +} +.form-wizard .form-wizard-steps li { + width: 33%; + float: left; + position: relative; +} +.form-wizard .form-wizard-steps li::after { + background-color: #f3f3f3; + content: ""; + height: 5px; + left: 0; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + width: 100%; + border-bottom: 1px solid #dddddd; + border-top: 1px solid #dddddd; +} +.form-wizard .form-wizard-steps li span { + background-color: #dddddd; + border-radius: 50%; + display: inline-block; + height: 40px; + line-height: 40px; + position: relative; + text-align: center; + width: 40px; + z-index: 1; +} +.form-wizard .form-wizard-steps li:last-child::after { + width: 50%; +} +.form-wizard .form-wizard-steps li.active span, +.form-wizard .form-wizard-steps li.activated span { + background-color: #d65470; + color: #ffffff; +} +.form-wizard .form-wizard-steps li.active::after, +.form-wizard .form-wizard-steps li.activated::after { + background-color: #d65470; + left: 50%; + width: 50%; + border-color: #d65470; +} +.form-wizard .form-wizard-steps li.activated::after { + width: 100%; + border-color: #d65470; +} +.form-wizard .form-wizard-steps li:last-child::after { + left: 0; +} +.form-wizard .wizard-password-eye { + position: absolute; + right: 32px; + top: 50%; + transform: translateY(-50%); + cursor: pointer; +} +@keyframes click-radio-wave { + 0% { + width: 25px; + height: 25px; + opacity: 0.35; + position: relative; + } + 100% { + width: 60px; + height: 60px; + margin-left: -15px; + margin-top: -15px; + opacity: 0; + } +} +@media screen and (max-width: 767px) { + .wizard-content-left { + height: auto; + } +} diff --git a/src/db/courseDB.js b/src/db/courseDB.js index be874e7..26a0f7b 100644 --- a/src/db/courseDB.js +++ b/src/db/courseDB.js @@ -13,8 +13,8 @@ const courseSchema = new mongoose.Schema({ maxLength: 30, }, longDescription: { - name: String, - values: mongoose.Schema.Types.Mixed, + type: mongoose.Schema.Types.Map, // [Issue: #26][Fix] | Should contain type instead of values + of: mongoose.Schema.Types.Mixed, }, duration: { type: Number, @@ -22,7 +22,7 @@ const courseSchema = new mongoose.Schema({ }, durationType: { type: String, - enum: ["hours", "minutes", "seconds"], + enum: ["years", "months", "days", "hours", "minutes", "seconds"], }, difficulty: { type: String, diff --git a/src/js/coures-create.js b/src/js/coures-create.js new file mode 100644 index 0000000..e69de29 diff --git a/src/views/course-create.ejs b/src/views/course-create.ejs new file mode 100644 index 0000000..ad3b9e1 --- /dev/null +++ b/src/views/course-create.ejs @@ -0,0 +1,226 @@ + + + + + + + + + + Create Course + + + +
+
+
+
+

Create Course

+
+
+ <% if (messageError && messageError.length > 0) { %> +
+ <%= messageError[0] %> +
+ <% } %> + <% if (messageSuccess && messageSuccess.length > 0) { %> +
+ <%= messageSuccess[0] %> +
+ <% } %> +
+
+
+ +
+

Fill all form field to go next step

+
    +
  • 1
  • +
  • 2
  • +
  • 3
  • +
+
+
+
Course Information
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ Next +
+
+
+
Account Information
+
+ + +
+
+
+
Duration Type*
+
+
+ +
+
+
+
+ Previous + Next +
+
+
+
+
Difficulty Type*
+
+
+ +
+
+
+
+
Upload Image*
+
+
+ +
+
+
+
+
+ Previous + +
+
+
+
+
+
+
+ + + + \ No newline at end of file