diff --git a/app.js b/app.js index 9bd6875..4a37b8e 100644 --- a/app.js +++ b/app.js @@ -50,6 +50,8 @@ app.get('/', (req, res) => { }); app.get('/signup', (req, res) => { + const ignore = ["User not found", "Incorrect password"]; + if (ignore.includes(req.session.errMessage)) req.session.errMessage = ""; res.render('signup', { errMessage: req.session.errMessage }); return res.status(status.Ok); }); diff --git a/src/auth/authentication.js b/src/auth/authentication.js index db53450..261b5a4 100644 --- a/src/auth/authentication.js +++ b/src/auth/authentication.js @@ -39,8 +39,6 @@ module.exports = (users) => { return res.redirect("/login"); } - console.log("User logged in successfully"); - console.log("User email: " + req.body.email); req.session.authenticated = true; req.session.userId = user._id; req.session.email = req.body.email; @@ -53,7 +51,7 @@ module.exports = (users) => { router.post("/signup", async (req, res) => { const userSchema = joi.object({ email: joi.string().email().required(), - // name: joi.string().alphanum().max(20).required(), + name: joi.string().alphanum().max(20).required(), password: joi.string().max(20).min(8).required(), repassword: joi.string().max(20).min(8).required(), }); @@ -76,13 +74,13 @@ module.exports = (users) => { users.insertOne({ email: req.body.email, - // name: req.body.name, + name: req.body.name, password: hashedPassword, }).then((results, err) => { if (err) { - res.status(status.InternalServerError); console.error(err); - return res.send("Internal server error"); + res.session.errMessage = "Internal server error"; + return res.status(status.InternalServerError).redirect("/signup"); } req.session.authenticated = true; @@ -90,8 +88,7 @@ module.exports = (users) => { req.session.userId = results.insertedId; req.session.errMessage = ""; - res.status(status.Ok); - return res.redirect("/home"); + return res.status(status.Ok).redirect("/home"); }); }); diff --git a/src/public/scripts/profile.js b/src/public/scripts/profile.js new file mode 100644 index 0000000..d803378 --- /dev/null +++ b/src/public/scripts/profile.js @@ -0,0 +1,75 @@ +/** + * lockAccount resets inputs and disabled inputs + */ +function lockAccount() { + // Clear unsaved inputs on page load (refresh doesnt clear them) + document.getElementById("account-form").reset(); + + document.getElementById("save-account").disabled = true; + document.getElementById("email").disabled = true; + document.getElementById("name").disabled = true; + document.getElementById("password").disabled = true; + document.getElementById("repassword").disabled = true; + document.getElementById("save-account").classList.add("cursor-not-allowed"); + + document.getElementById("edit-account").innerHTML = "Edit"; + document.getElementById("edit-account").onclick = unlockAccount; +} + +/** + * lockPersonal resets inputs and disabled inputs + */ +function lockPersonal() { + // Clear unsaved inputs on page load (refresh doesnt clear them) + document.getElementById("personal-form").reset(); + + document.getElementById("save-personal").disabled = true; + document.getElementById("dob").disabled = true; + document.getElementById("education").disabled = true; + document.getElementById("ms-single").disabled = true; + document.getElementById("ms-married").disabled = true; + document.getElementById("ms-divorced").disabled = true; + document.getElementById("ms-widowed").disabled = true; + document.getElementById("save-personal").classList.add("cursor-not-allowed"); + + document.getElementById("edit-personal").innerHTML = "Edit"; + document.getElementById("edit-personal").onclick = unlockPersonal; +} + +/** + * unlockAccount removes disabled from inputs and + * allows users to edit their profile. + */ +function unlockAccount() { + document.getElementById("save-account").disabled = false; + // document.getElementById("email").disabled = false; + document.getElementById("name").disabled = false; + document.getElementById("password").disabled = false; + document.getElementById("repassword").disabled = false; + document.getElementById("save-account").classList.remove("cursor-not-allowed"); + + document.getElementById("edit-account").innerHTML = "Cancel changes"; + document.getElementById("edit-account").onclick = lockAccount; +} + +/** + * unlocPersonal removes disabled from inputs and + * allows users to edit their personal information. + */ +function unlockPersonal() { + document.getElementById("save-personal").disabled = false; + document.getElementById("dob").disabled = false; + document.getElementById("education").disabled = false; + document.getElementById("ms-single").disabled = false; + document.getElementById("ms-married").disabled = false; + document.getElementById("ms-divorced").disabled = false; + document.getElementById("ms-widowed").disabled = false; + document.getElementById("save-personal").classList.remove("cursor-not-allowed"); + + document.getElementById("edit-personal").innerHTML = "Cancel changes"; + document.getElementById("edit-personal").onclick = lockPersonal; +} + +// On page load, ensure forms are locked and reset +lockAccount(); +// lockPersonal(); diff --git a/src/router/user.js b/src/router/user.js index d592f37..9daa7cb 100644 --- a/src/router/user.js +++ b/src/router/user.js @@ -1,5 +1,7 @@ const status = require("../util/statuses"); +const bcrypt = require('bcrypt'); const joi = require("joi"); +const salt = 12; module.exports = (middleware, users, plans) => { const router = require("express").Router(); @@ -94,7 +96,7 @@ module.exports = (middleware, users, plans) => { }); router.get('/profile', (req, res) => { - res.render('profiles', { user: req.user }); + res.render('profile', { user: req.user, errMessage: req.session.errMessage }); return res.status(status.Ok); }); @@ -111,7 +113,7 @@ module.exports = (middleware, users, plans) => { router.post('/questionnaire', (req, res) => { // console.log("Questionnaire POST body:", req.body); - + const questionnaireSchema = joi.object({ dob: joi.date().required(), education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(), @@ -167,5 +169,58 @@ module.exports = (middleware, users, plans) => { }); }); + router.post("/updateAccount", async (req, res) => { + const accountSchema = joi.object({ + email: joi.string().email(), + name: joi.string().alphanum().max(20), + password: joi.string().max(20).min(8), + repassword: joi.string().max(20).min(8), + }); + + const valid = accountSchema.validate(req.body); + + if (valid.err) { + req.session.errMessage = "Invalid input", + res.status(status.BadRequest); + return res.redirect("/profile"); + } + + let update = { + // email: req.body.email, + name: req.body.name, + }; + + if ((req.body.password != "") && (req.body.password != req.body.repassword)) { + req.session.errMessage = "New passwords must match"; + res.status(status.BadRequest); + return res.redirect("/profile"); + } else if (req.body.password != "") { + let hashedPassword = await bcrypt.hashSync(req.body.password, salt); + update.password = hashedPassword; + } + + users.updateOne( + { email: req.session.email }, + { $set: update } + ).then((result) => { + if (result.matchedCount === 0) { + console.log(`User not found during account update: ${req.session.email}`); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); + return; + } + if (result.modifiedCount === 0 && result.matchedCount === 1) { + console.log(`User account data unchanged (already up-to-date): ${req.session.email}`); + } + + req.session.errMessage = ""; + return res.status(status.Ok).redirect("/profile"); + }).catch(err => { + console.error("Error updating account in database:", err); + req.session.errMessage = "An error occurred while saving your information. Please try again."; + return res.status(status.InternalServerError).redirect("/profile"); + }); + }); + return router; }; diff --git a/src/views/profile.ejs b/src/views/profile.ejs new file mode 100644 index 0000000..34f0bc2 --- /dev/null +++ b/src/views/profile.ejs @@ -0,0 +1,90 @@ +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> + +
+

Welcome <%= user.name %>

+ + <% if (errMessage != "") { %> +
<%= errMessage %>
+ <% } %> + +
+
+

Account settings

+ +
+
+ + + + + + + + + + + + + +
+ +
+
+
+ + +
+
+ + + +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/signup.ejs b/src/views/signup.ejs index 7411268..97f1347 100644 --- a/src/views/signup.ejs +++ b/src/views/signup.ejs @@ -13,6 +13,12 @@ class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 " placeholder="Enter Email" /> +
+ + +