Jelajahi Sumber

Merge branch 'development'

Lee Morgan 5 tahun lalu
induk
melakukan
a757552c5c
38 mengubah file dengan 437 tambahan dan 159 penghapusan
  1. 2 1
      .gitignore
  2. 2 3
      controllers/emailVerification.js
  3. 2 2
      controllers/helper.js
  4. 2 2
      controllers/ingredientData.js
  5. 85 3
      controllers/merchantData.js
  6. 1 1
      controllers/orderData.js
  7. 1 1
      controllers/otherData.js
  8. 4 10
      controllers/passwordReset.js
  9. 3 3
      controllers/recipeData.js
  10. 2 4
      controllers/renderer.js
  11. 1 1
      controllers/transactionData.js
  12. 2 0
      middleware.js
  13. 2 3
      models/merchant.js
  14. 2 0
      routes.js
  15. 49 0
      views/dashboardPage/dashboard.css
  16. 1 0
      views/dashboardPage/dashboard.ejs
  17. 10 0
      views/dashboardPage/ejs/menu.ejs
  18. 39 0
      views/dashboardPage/ejs/strands/account.ejs
  19. 45 35
      views/dashboardPage/js/classes/Merchant.js
  20. 3 1
      views/dashboardPage/js/classes/Order.js
  21. 5 2
      views/dashboardPage/js/classes/Recipe.js
  22. 18 26
      views/dashboardPage/js/dashboard.js
  23. 1 1
      views/dashboardPage/js/sidebars/editIngredient.js
  24. 1 1
      views/dashboardPage/js/sidebars/editRecipe.js
  25. 3 3
      views/dashboardPage/js/sidebars/newIngredient.js
  26. 2 1
      views/dashboardPage/js/sidebars/newOrder.js
  27. 3 3
      views/dashboardPage/js/sidebars/newRecipe.js
  28. 5 3
      views/dashboardPage/js/sidebars/orderFilter.js
  29. 5 3
      views/dashboardPage/js/sidebars/transactionFilter.js
  30. 101 0
      views/dashboardPage/js/strands/account.js
  31. 8 6
      views/dashboardPage/js/strands/analytics.js
  32. 1 1
      views/dashboardPage/js/strands/home.js
  33. 6 4
      views/dashboardPage/js/strands/recipeBook.js
  34. 16 12
      views/dashboardPage/sidebars.css
  35. 1 1
      views/otherPages/login.ejs
  36. 1 1
      views/otherPages/register.ejs
  37. 1 21
      views/otherPages/style.css
  38. 1 0
      views/shared/shared.css

+ 2 - 1
.gitignore

@@ -1,2 +1,3 @@
 node_modules/
-dist/
+dist/
+bundle.js

+ 2 - 3
controllers/emailVerification.js

@@ -13,7 +13,7 @@ module.exports = {
                     subject: "Email verification",
                     html: verifyEmail({
                         name: merchant.name,
-                        link: `${process.env.SITE}/verify/${merchant._id}/${merchant.verifyId}`,
+                        link: `${process.env.SITE}/verify/${merchant._id}/${merchant.session.sessionId}`,
                     })
                 };
                 mailgun.messages().send(mailgunData, (err, body)=>{});
@@ -59,11 +59,10 @@ module.exports = {
     verify: function(req, res){
         Merchant.findOne({_id: req.params.id})
             .then((merchant)=>{
-                if(req.params.code !== merchant.verifyId){
+                if(req.params.code !== merchant.session.sessionId){
                     throw "UNABLE TO VERIFY EMAIL ADDRESS.  INCORRECT LINK";
                 }
 
-                merchant.verifyId = undefined;
                 merchant.status.splice(merchant.status.indexOf("unverified"), 1);
 
                 const mailgunList = mailgun.lists("clientsupport@mail.thesubline.com");

+ 2 - 2
controllers/helper.js

@@ -87,7 +87,7 @@ module.exports = {
                 return Transaction.create(transactions);
             })
             .catch((err)=>{
-                req.session.error = "ERROR: UNABLE TO RETRIEVE DATA FROM CLOVER";
+                req.session.error = "ERROR: UNABLE TO RETRIEVE DATA";
                 return res.redirect("/");
             });
     },
@@ -179,7 +179,7 @@ module.exports = {
                 return transactions;
             })
             .catch((err)=>{
-                return "ERROR: UNABLE TO UPDATE TRANSACTION DATA";
+                return "ERROR: UNABLE TO UPDATE DATA";
             });
     },
 

+ 2 - 2
controllers/ingredientData.js

@@ -112,7 +112,7 @@ module.exports = {
                 if(err.name === "ValidationError"){
                     return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
                 }
-                return res.json("ERROR: UNABLE TO UPDATE THE INGREDIENT");
+                return res.json("ERROR: UNABLE TO UPDATE DATA");
             });
     },
 
@@ -228,7 +228,7 @@ module.exports = {
                 if(err.name === "ValidationError"){
                     return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
                 }
-                return res.json("ERROR: UNABLE TO RETRIEVE USER DATA");
+                return res.json("ERROR: UNABLE TO RETRIEVE DATA");
             });
     }
 }

+ 85 - 3
controllers/merchantData.js

@@ -2,12 +2,14 @@ const Merchant = require("../models/merchant");
 const InventoryAdjustment = require("../models/inventoryAdjustment");
 
 const helper = require("./helper.js");
+const verifyEmail = require("../emails/verifyEmail.js");
 
 const bcrypt = require("bcryptjs");
+const mailgun = require("mailgun-js")({apiKey: process.env.MG_SUBLINE_APIKEY, domain: "mail.thesubline.net"});
 
 module.exports = {
     /*
-    POST - Create a new merchant with no POS system1
+    POST - Create a new merchant with no POS system
     req.body = {
         name: retaurant name,
         email: registration email,
@@ -49,7 +51,6 @@ module.exports = {
             status: ["unverified"],
             inventory: [],
             recipes: [],
-            verifyId: helper.generateId(15),
             session: {
                 sessionId: helper.generateId(25),
                 expiration: expirationDate
@@ -152,7 +153,7 @@ module.exports = {
 
                     return merchant.save();
                 }else{
-                    req.session.error = "ERROR: UNABLE TO RETRIEVE USER DATA";
+                    req.session.error = "ERROR: UNABLE TO RETRIEVE DATA";
                     return res.redirect("/");
                 }
             })
@@ -169,5 +170,86 @@ module.exports = {
                 }
                 return res.json("ERROR: UNABLE TO UPDATE YOUR PASSWORD");
             });
+    },
+
+    /*
+    PUT: Update merchant data
+    req.body = {
+        email: String (merchant email address)
+    },
+    response = Merchant
+    */
+    updateData: async function(req, res){
+        if(req.body.email !== res.locals.merchant.email){
+            let merchantCheck = await Merchant.findOne({email: req.body.email});
+            if(merchantCheck !== null){
+                return res.json("USER WITH THIS EMAIL ADDRESS ALREADY EXISTS");
+            }
+
+            res.locals.merchant.email = req.body.email;
+            res.locals.merchant.status.push("unverified");
+
+            const mailgunData = {
+                from: "The Subline <clientsupport@thesubline.net>",
+                to: res.locals.merchant.email,
+                subject: "Email Verification",
+                html: verifyEmail({
+                    name: res.locals.merchant.name,
+                    link: `${process.env.SITE}/verify/${res.locals.merchant._id}/${res.locals.merchant.sessionId}`
+                })
+            };
+            mailgun.messages().send(mailgunData, (err, body)=>{});
+        }
+
+        res.locals.merchant.save()
+            .then((merchant)=>{
+                return res.json(merchant);
+            })
+            .catch((err)=>{
+                if(err.name === "ValidationError"){
+                    return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
+                }
+                return res.json("ERROR: UNABLE TO UPDATE DATA");
+            });
+    },
+
+    /*
+    PUT: Update merchant password with current password
+    req.body = {
+        current: String (current merchant password),
+        new: String (new password),
+        confirm: String (new password again for confirmation)
+    }
+    response = {redirect: String (link to redirect to)}
+    */
+    changePassword: function(req, res){
+        if(req.body.new !== req.body.confirm){
+            return res.json("PASSWORDS DO NOT MATCH");
+        }
+
+        bcrypt.compare(req.body.current, res.locals.merchant.password, (err, result)=>{
+            if(result === true){
+                let salt = bcrypt.genSaltSync(10);
+                let hash = bcrypt.hashSync(req.body.new, salt);
+
+                res.locals.merchant.password = hash;
+
+                let newExpiration = new Date();
+                newExpiration.setDate(newExpiration.getDate() + 90);
+                res.locals.merchant.session.sessionId = helper.generateId(25);
+                res.locals.merchant.session.expiration = newExpiration;
+
+                res.locals.merchant.save()
+                    .then((merchant)=>{
+                        req.session.error = "PLEASE LOG IN";
+                        return res.json({redirect: `http://${process.env.SITE}/login`});
+                    })
+                    .catch((err)=>{
+                        return res.json("ERROR: UNABLE TO UPDATE PASSWORD");
+                    });
+            }else{
+                return res.json("INCORRECT PASSWORD");
+            }
+        });
     }
 }

+ 1 - 1
controllers/orderData.js

@@ -54,7 +54,7 @@ module.exports = {
                 return res.json(orders);
             })
             .catch((err)=>{
-                return res.json("ERROR: UNABLE TO RETRIEVE YOUR ORDERS");
+                return res.json("ERROR: UNABLE TO RETRIEVE DATA");
             });
     },
 

+ 1 - 1
controllers/otherData.js

@@ -30,7 +30,7 @@ module.exports = {
                 }
             })
             .catch((err)=>{
-                req.session.error = "ERROR: UNABLE TO RETRIEVE USER DATA";
+                req.session.error = "ERROR: UNABLE TO RETRIEVE DATA";
 
                 return res.redirect("/");
             });

+ 4 - 10
controllers/passwordReset.js

@@ -18,8 +18,6 @@ module.exports = {
                     req.session.error = "USER WITH THIS EMAIL DOES NOT EXIST";
                     return res.redirect("/");
                 }
-
-                merchant.verifyId = helper.generateId(15);
                 
                 const mailgunData = {
                     from: "The Subline <clientsupport@thesubline.net>",
@@ -27,14 +25,11 @@ module.exports = {
                     subject: "Password Reset",
                     html: passwordReset({
                         name: merchant.name,
-                        link: `${process.env.SITE}/reset/${merchant._id}/${merchant.verifyId}`
+                        link: `${process.env.SITE}/reset/${merchant._id}/${merchant.session.sessionId}`
                     })
                 };
                 mailgun.messages().send(mailgunData, (err, body)=>{});
 
-                return merchant.save();
-            })
-            .then((merchant)=>{
                 req.session.success = "PASSWORD RESET EMAIL SENT";
                 return res.redirect("/");
             })
@@ -51,26 +46,25 @@ module.exports = {
     resetPassword: function(req, res){
         Merchant.findOne({_id: req.body.id})
             .then((merchant)=>{
-                if(merchant.verifyId !== req.body.code){
+                if(merchant.session.sessionId !== req.body.code){
                     req.session.error = "YOUR ACCOUNT COULD NOT BE VERIFIED.  PLEASE CONTACT US IF THE PROBLEM PERSISTS.";
                     return res.redirect("/");
                 }
 
                 if(req.body.password !== req.body.confirmPassword){
                     req.session.error = "PASSWORDS DO NOT MATCH";
-                    return res.redirect(`/reset/${merchant._id}/${merchant.verifyId}`);
+                    return res.redirect(`/reset/${merchant._id}/${merchant.session.sessionId}`);
                 }
 
                 if(req.body.password.length < 10){
                     req.session.error = "PASSWORD MUST CONTAIN AT LEAST 10 CHARACTERS";
-                    return res.redirect(`/reset/${merchant._id}/${merchant.verifyId}`);
+                    return res.redirect(`/reset/${merchant._id}/${merchant.session.sessionId}`);
                 }
 
                 const salt = bcrypt.genSaltSync(10);
                 const hash = bcrypt.hashSync(req.body.password, salt);
 
                 merchant.password = hash;
-                merchant.verifyId = undefined;
 
                 return merchant.save();
             })

+ 3 - 3
controllers/recipeData.js

@@ -84,7 +84,7 @@ module.exports = {
                 if(err.name === "ValidationError"){
                     return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
                 }
-                return res.json("ERROR: UNABLE TO UPDATE RECIPE");
+                return res.json("ERROR: UNABLE TO UPDATE DATA");
             });
     },
 
@@ -112,7 +112,7 @@ module.exports = {
                 if(err.name === "ValidationError"){
                     return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
                 }
-                return res.json("ERROR: UNABLE TO RETRIEVE USER DATA");
+                return res.json("ERROR: UNABLE TO RETRIEVE DATA");
             });
     },
 
@@ -183,7 +183,7 @@ module.exports = {
 
                     let exists = false;
                     for(let j = 0; j < ingredients.length; j++){
-                        if(ingredients[j].name === array[i][locations.ingredient]){
+                        if(ingredients[j].name.toLowerCase() === array[i][locations.ingredient].toLowerCase()){
                             currentRecipe.ingredients.push({
                                 ingredient: ingredients[j].id,
                                 quantity: helper.convertQuantityToBaseUnit(array[i][locations.amount], ingredients[j].unit)

+ 2 - 4
controllers/renderer.js

@@ -29,7 +29,6 @@ module.exports = {
     Renders inventoryPage
     */
     displayDashboard: function(req, res){
-        let merchant2 = {};
         res.locals.merchant
             .populate("inventory.ingredient")
             .populate("recipes")
@@ -75,12 +74,11 @@ module.exports = {
                 return res.render("dashboardPage/dashboard", {merchant: res.locals.merchant, transactions: transactions});
             })
             .catch((err)=>{
-                //TODO: add banners to the necessary pages
                 if(err === "unverified"){
                     req.session.error = "PLEASE VERIFY YOUR EMAIL ADDRESS";
-                    return res.redirect(`/verify/email/${merchant2._id}`);
+                    return res.redirect(`/verify/email/${res.locals.merchant._id}`);
                 }
-                req.session.error = "ERROR: UNABLE TO RETRIEVE USER DATA";
+                req.session.error = "ERROR: UNABLE TO RETRIEVE DATA";
                 return res.redirect("/");
             });
     },

+ 1 - 1
controllers/transactionData.js

@@ -50,7 +50,7 @@ module.exports = {
                 return res.json(transactions);
             })
             .catch((err)=>{
-                return res.json("ERROR: UNABLE TO RETRIEVE YOUR TRANSACTIONS");
+                return res.json("ERROR: UNABLE TO RETRIEVE DATA");
             });
     },
 

+ 2 - 0
middleware.js

@@ -1,5 +1,7 @@
 const Merchant = require("./models/merchant.js")
 
+const helper = require("./controllers/helper.js");
+
 module.exports = {
     verifySession: function(req, res, next){
         if(req.session.user === undefined) {

+ 2 - 3
models/merchant.js

@@ -3,7 +3,7 @@ const isSanitary = require("../controllers/helper.js").isSanitary;
 const mongoose = require("mongoose");
 
 let emailValid = (value)=>{
-    /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value);
+    return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value);
 }
 
 const MerchantSchema = new mongoose.Schema({
@@ -69,8 +69,7 @@ const MerchantSchema = new mongoose.Schema({
             index: true
         },
         expiration: Date
-    },
-    verifyId: String
+    }
 });
 
 module.exports = mongoose.model("Merchant", MerchantSchema);

+ 2 - 0
routes.js

@@ -26,6 +26,8 @@ module.exports = function(app){
     app.post("/merchant/create/none", merchantData.createMerchantNone);
     app.put("/merchant/ingredients/update", session, merchantData.updateIngredientQuantities); //also updates some data in ingredients
     app.post("/merchant/password", merchantData.updatePassword); //TODO: change to work with session
+    app.put("/merchant/update", session, merchantData.updateData);
+    app.put("/merchant/password", session, merchantData.changePassword);
 
     //Ingredients
     app.post("/ingredients/create", session, ingredientData.createIngredient);  //also adds to merchant

+ 49 - 0
views/dashboardPage/dashboard.css

@@ -567,6 +567,55 @@ Transactions Strand
         margin-top: 50px;
     }
 
+/*
+ACCOUNT STRAND
+*/
+#accountStrand{
+    padding: 15px;
+    box-sizing: border-box;
+}
+
+    .horizontal{
+        display: flex;
+        justify-content: space-around;
+    }
+
+    .vertical{
+        display: flex;
+        flex-direction: column;
+    }
+
+    #accountUpdate{
+        max-width: 500px;
+    }
+
+    #accountShowPassword{
+        max-width: 300px;
+    }
+
+    #changePasswordBox{
+        flex-direction: column;
+        padding: 25px;
+        background: rgb(240, 252, 255);
+        border: 2px solid black;
+        border-radius: 10px;
+    }
+
+        #changePasswordBox label{
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin: 5px;
+        }
+
+        #changePasswordBox p{
+            color: red;
+        }
+
+        #accountButtonBox{
+            margin: 0;
+        }
+
 /*
 Modal
 */

+ 1 - 0
views/dashboardPage/dashboard.ejs

@@ -32,6 +32,7 @@
             <% include ./ejs/strands/analytics.ejs %>
             <% include ./ejs/strands/orders.ejs %>
             <% include ./ejs/strands/transactions.ejs %>
+            <% include ./ejs/strands/account.ejs %>
         </div>
 
         <div id="sidebarDiv" class="sidebarHide">

+ 10 - 0
views/dashboardPage/ejs/menu.ejs

@@ -66,6 +66,14 @@
         <p>TRANSACTIONS</p>
     </button>
 
+    <button class="menuButton" id="accountBtn">
+        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+            <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
+            <circle cx="12" cy="7" r="4"></circle>
+        </svg>
+        <p>ACCOUNT</p>
+    </button>
+
     <a class="menuButton" href="/logout">
         <svg width="25" height="25" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
             <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
@@ -74,4 +82,6 @@
         </svg>
         <p>LOGOUT</p>
     </a>
+
+    <a href="/help">Help</a>
 </div>

+ 39 - 0
views/dashboardPage/ejs/strands/account.ejs

@@ -0,0 +1,39 @@
+<div id="accountStrand" class="strand">
+    <div class="strandHead">
+        <h1 id="accountStrandTitle" class="strandTitle"></h1>
+    </div>
+
+    <div class="horizontal">
+        <div class="vertical">
+            <label>EMAIL
+                <input id="accountEmail" type="email">
+            </label>
+
+            <button id="accountUpdate" class="button">UPDATE</button>
+        </div>
+
+        <button id="accountShowPassword" class="button">CHANGE PASSWORD</button>
+
+        <div id="changePasswordBox" style="display:none;">
+            <label>CURRENT PASSWORD
+                <input id="accountCurrentPassword" type="password">
+            </label>
+
+            <label>NEW PASSWORD
+                <input id="accountNewPassword" type="password">
+            </label>
+
+            <label>CONFIRM NEW PASSWORD
+                <input id="accountConfirmPassword" type="password">
+            </label>
+
+            <div id="accountButtonBox" class="buttonBox">
+                <button id="cancelPasswordChange" class="button">CANCEL</button>
+
+                <button id="changePasswordButton" class="button">CHANGE PASSWORD</button>
+            </div>
+
+            <p>* This will log you out of all current sessions</p>
+        </div>
+    </div>
+</div>

+ 45 - 35
views/dashboardPage/js/classes/Merchant.js

@@ -1,3 +1,14 @@
+const Ingredient = require("./Ingredient.js");
+const Recipe = require("./Recipe.js");
+const Transaction = require("./Transaction.js");
+const Order = require("./Order.js");
+
+const home = require("../strands/home.js");
+const ingredients = require("../strands/ingredients.js");
+const recipeBook = require("../strands/recipeBook");
+const analytics = require("../strands/analytics.js");
+const orders = require("../strands/orders");
+
 class MerchantIngredient{
     constructor(ingredient, quantity){
         this._quantity = quantity;
@@ -66,9 +77,9 @@ class MerchantIngredient{
 }
 
 class Merchant{
-    constructor(oldMerchant, transactions, modules){
-        this._modules = modules;
+    constructor(oldMerchant, transactions){
         this._name = oldMerchant.name;
+        this._email = oldMerchant.email;
         this._pos = oldMerchant.pos;
         this._ingredients = [];
         this._recipes = [];
@@ -77,7 +88,7 @@ class Merchant{
         
         //populate ingredients
         for(let i = 0; i < oldMerchant.inventory.length; i++){
-            const ingredient = new modules.Ingredient(
+            const ingredient = new Ingredient(
                 oldMerchant.inventory[i].ingredient._id,
                 oldMerchant.inventory[i].ingredient.name,
                 oldMerchant.inventory[i].ingredient.category,
@@ -111,7 +122,7 @@ class Merchant{
                 }
             }
 
-            this._recipes.push(new this._modules.Recipe(
+            this._recipes.push(new Recipe(
                 oldMerchant.recipes[i]._id,
                 oldMerchant.recipes[i].name,
                 oldMerchant.recipes[i].price,
@@ -122,7 +133,7 @@ class Merchant{
 
         //populate transactions
         for(let i = 0; i < transactions.length; i++){
-            this._transactions.push(new modules.Transaction(
+            this._transactions.push(new Transaction(
                 transactions[i]._id,
                 transactions[i].date,
                 transactions[i].recipes,
@@ -131,10 +142,6 @@ class Merchant{
         }
     }
 
-    get modules(){
-        return this._modules;
-    }
-
     get name(){
         return this._name;
     }
@@ -143,6 +150,14 @@ class Merchant{
         this._name = name;
     }
 
+    get email(){
+        return this._email;
+    }
+
+    set email(email){
+        this._email = email;
+    }
+
     get pos(){
         return this._pos;
     }
@@ -164,7 +179,7 @@ class Merchant{
     defaultUnit: String
     */
     addIngredient(ingredient, quantity, defaultUnit){
-        const createdIngredient = new this._modules.Ingredient(
+        const createdIngredient = new Ingredient(
             ingredient._id,
             ingredient.name,
             ingredient.category,
@@ -177,8 +192,8 @@ class Merchant{
         const merchantIngredient = new MerchantIngredient(createdIngredient, quantity);
         this._ingredients.push(merchantIngredient);
 
-        this._modules.home.isPopulated = false;
-        this._modules.ingredients.isPopulated = false;
+        home.isPopulated = false;
+        ingredients.isPopulated = false;
     }
 
     removeIngredient(ingredient){
@@ -189,8 +204,8 @@ class Merchant{
 
         this._ingredients.splice(index, 1);
 
-        this._modules.home.isPopulated = false;
-        this._modules.ingredients.isPopulated = false;
+        home.isPopulated = false;
+        ingredients.isPopulated = false;
     }
 
     getIngredient(id){
@@ -206,11 +221,11 @@ class Merchant{
     }
 
     addRecipe(id, name, price, ingredients){
-        let recipe = new this._modules.Recipe(id, name, price, ingredients, this);
+        let recipe = new Recipe(id, name, price, ingredients, this);
 
         this._recipes.push(recipe);
 
-        this._modules.recipeBook.isPopulated = false;
+        recipeBook.isPopulated = false;
     }
 
     removeRecipe(recipe){
@@ -221,7 +236,7 @@ class Merchant{
 
         this._recipes.splice(index, 1);
 
-        this._modules.recipeBook.isPopulated = false;
+        recipeBook.isPopulated = false;
     }
 
     /*
@@ -258,18 +273,13 @@ class Merchant{
             }
         }
 
-        this._modules.recipeBook.isPopulated = false;
+        recipeBook.isPopulated = false;
     }
 
     get transactions(){
         return this._transactions;
     }
 
-    //TODO: remove this, just for testing purposes
-    clearTransactions(){
-        this._transactions = [];
-    }
-
     getTransactions(from = 0, to = new Date()){
         if(merchant._transactions.length <= 0){
             return [];
@@ -285,7 +295,7 @@ class Merchant{
     }
 
     addTransaction(transaction){
-        transaction = new this._modules.Transaction(
+        transaction = new Transaction(
             transaction._id,
             transaction.date,
             transaction.recipes,
@@ -322,9 +332,9 @@ class Merchant{
             }
         }
 
-        this._modules.home.isPopulated = false;
-        this._modules.ingredients.isPopulated = false;
-        this._modules.analytics.newData = true;
+        home.isPopulated = false;
+        ingredients.isPopulated = false;
+        analytics.newData = true;
     }
 
     removeTransaction(transaction){
@@ -358,9 +368,9 @@ class Merchant{
             }
         }
 
-        this._modules.home.isPopulated = false;
-        this._modules.ingredients.isPopulated = false;
-        this._modules.analytics.newData = true;
+        home.isPopulated = false;
+        ingredients.isPopulated = false;
+        analytics.newData = true;
     }
 
     get orders(){
@@ -372,7 +382,7 @@ class Merchant{
     }
 
     addOrder(data, isNew = false){
-        let order = new this._modules.Order(
+        let order = new Order(
             data._id,
             data.name,
             data.date,
@@ -395,8 +405,8 @@ class Merchant{
             }
         }
 
-        this._modules.ingredients.isPopulated = false;
-        this._modules.orders.isPopulated = false;
+        ingredients.isPopulated = false;
+        orders.isPopulated = false;
     }
 
     removeOrder(order){
@@ -416,8 +426,8 @@ class Merchant{
             }
         }
 
-        this._modules.ingredients.isPopulated = false;
-        this._modules.orders.isPopulated = false;
+        ingredients.isPopulated = false;
+        orders.isPopulated = false;
     }
 
     get units(){

+ 3 - 1
views/dashboardPage/js/classes/Order.js

@@ -1,3 +1,5 @@
+const ingredients = require("../strands/ingredients.js");
+
 class OrderIngredient{
     constructor(ingredient, quantity, pricePerUnit){
         this._ingredient = ingredient;
@@ -127,7 +129,7 @@ class Order{
             
         }
 
-        this._parent.modules.ingredients.isPopulated = false;
+        ingredients.isPopulated = false;
     }
 
     get id(){

+ 5 - 2
views/dashboardPage/js/classes/Recipe.js

@@ -1,3 +1,6 @@
+const recipeBook = require("../strands/recipeBook.js");
+const analytics = require("../strands/analytics.js");
+
 class RecipeIngredient{
     constructor(ingredient, quantity){
         this._ingredient = ingredient;
@@ -127,8 +130,8 @@ class Recipe{
         let recipeIngredient = new RecipeIngredient(ingredient, quantity);
         this._ingredients.push(recipeIngredient);
 
-        this._parent.modules.recipeBook.isPopulated = false;
-        this._parent.modules.analytics.isPopulated = false;
+        recipeBook.isPopulated = false;
+        analytics.isPopulated = false;
     }
 
     removeIngredients(){

+ 18 - 26
views/dashboardPage/js/dashboard.js

@@ -4,6 +4,7 @@ const recipeBook = require("./strands/recipeBook.js");
 const analytics = require("./strands/analytics.js");
 const orders = require("./strands/orders.js");
 const transactions = require("./strands/transactions.js");
+const account = require("./strands/account.js");
 
 const ingredientDetails = require("./sidebars/ingredientDetails.js");
 const newIngredient = require("./sidebars/newIngredient.js");
@@ -20,23 +21,8 @@ const transactionDetails = require("./sidebars/transactionDetails.js");
 const transactionFilter = require("./sidebars/transactionFilter.js");
 
 const Merchant = require("./classes/Merchant.js");
-const Ingredient = require("./classes/Ingredient.js");
-const Recipe = require("./classes/Recipe.js");
-const Order = require("./classes/Order.js");
-const Transaction = require("./classes/Transaction.js");
-
-merchant = new Merchant(data.merchant, data.transactions, {
-    home: home,
-    ingredients: ingredients,
-    transactions: transactions,
-    recipeBook: recipeBook,
-    analytics: analytics,
-    orders: orders,
-    Ingredient: Ingredient,
-    Recipe: Recipe,
-    Transaction: Transaction,
-    Order: Order
-});
+
+merchant = new Merchant(data.merchant, data.transactions);
 
 controller = {
     openStrand: function(strand, data = undefined){
@@ -68,12 +54,12 @@ controller = {
             case "recipeBook":
                 activeButton = document.getElementById("recipeBookBtn");
                 document.getElementById("recipeBookStrand").style.display = "flex";
-                recipeBook.display(Recipe);
+                recipeBook.display();
                 break;
             case "analytics":
                 activeButton = document.getElementById("analyticsBtn");
                 document.getElementById("analyticsStrand").style.display = "flex";
-                analytics.display(Transaction);
+                analytics.display();
                 break;
             case "orders":
                 activeButton = document.getElementById("ordersBtn");
@@ -84,7 +70,12 @@ controller = {
                 activeButton = document.getElementById("transactionsBtn");
                 document.getElementById("transactionsStrand").style.display = "flex";
                 transactions.transactions = data;
-                transactions.display(Transaction);
+                transactions.display();
+                break;
+            case "account":
+                activeButton = document.getElementById("accountBtn");
+                document.getElementById("accountStrand").style.display = "flex";
+                account.display();
                 break;
         }
 
@@ -109,10 +100,10 @@ controller = {
 
         switch(sidebar){
             case "ingredientDetails":
-                ingredientDetails.display(data, ingredients);
+                ingredientDetails.display(data);
                 break;
             case "newIngredient":
-                newIngredient.display(Ingredient);
+                newIngredient.display();
                 break;
             case "editIngredient":
                 editIngredient.display(data);
@@ -124,13 +115,13 @@ controller = {
                 editRecipe.display(data);
                 break;
             case "addRecipe":
-                newRecipe.display(Recipe);
+                newRecipe.display();
                 break;
             case "orderDetails":
                 orderDetails.display(data);
                 break;
             case "orderFilter":
-                orderFilter.display(Order);
+                orderFilter.display();
                 break;
             case "newOrder":
                 newOrder.display();
@@ -142,10 +133,10 @@ controller = {
                 transactionDetails.display(data);
                 break;
             case "transactionFilter":
-                transactionFilter.display(Transaction);
+                transactionFilter.display();
                 break;
             case "newTransaction":
-                newTransaction.display(Transaction);
+                newTransaction.display();
                 break;
         }
 
@@ -325,5 +316,6 @@ document.getElementById("recipeBookBtn").onclick = ()=>{controller.openStrand("r
 document.getElementById("analyticsBtn").onclick = ()=>{controller.openStrand("analytics")};
 document.getElementById("ordersBtn").onclick = async ()=>{controller.openStrand("orders")};
 document.getElementById("transactionsBtn").onclick = ()=>{controller.openStrand("transactions", merchant.getTransactions())};
+document.getElementById("accountBtn").onclick = ()=>{controller.openStrand("account")};
 
 controller.openStrand("home");

+ 1 - 1
views/dashboardPage/js/sidebars/editIngredient.js

@@ -111,7 +111,7 @@ let editIngredient = {
             }
         })
         .catch((err)=>{
-            controller.createBanner("SOMETHING WENT WRONG, PLEASE REFRESH THE PAGE", "error");
+            controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
         })
         .finally(()=>{
             loader.style.display = "none";

+ 1 - 1
views/dashboardPage/js/sidebars/editRecipe.js

@@ -119,7 +119,7 @@ let editRecipe = {
                 }
             })
             .catch((err)=>{
-                controller.createBanner("SOMETHING WENT WRONG, PLEASE REFRESH THE PAGE", "error");
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
             })
             .finally(()=>{
                 loader.style.display = "none";

+ 3 - 3
views/dashboardPage/js/sidebars/newIngredient.js

@@ -1,5 +1,5 @@
 let newIngredient = {
-    display: function(Ingredient){
+    display: function(){
         const selector = document.getElementById("unitSelector");
 
         document.getElementById("newIngName").value = "";
@@ -9,7 +9,7 @@ let newIngredient = {
         selector.value = "g";
 
         selector.onchange = ()=>{this.unitChange()};
-        document.getElementById("submitNewIng").onclick = ()=>{this.submit(Ingredient)};
+        document.getElementById("submitNewIng").onclick = ()=>{this.submit()};
         document.getElementById("ingredientFileUpload").addEventListener("click", ()=>{controller.openModal("ingredientSpreadsheet")});
     },
 
@@ -105,7 +105,7 @@ let newIngredient = {
                 
             })
             .catch((err)=>{
-                controller.createBanner("SOMETHING WENT WRONG.  TRY REFRESHING THE PAGE", "error");
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
             })
             .finally(()=>{
                 loader.style.display = "none";

+ 2 - 1
views/dashboardPage/js/sidebars/newOrder.js

@@ -14,6 +14,7 @@ let newOrder = {
             ingredientList.removeChild(ingredientList.firstChild);
         }
 
+        merchant.ingredients.sort((a, b) => (a.ingredient.name > b.ingredient.name) ? 1 : -1);
         for(let i = 0; i < merchant.ingredients.length; i++){
             let ingredient = document.createElement("button");
             ingredient.classList = "choosable";
@@ -96,7 +97,7 @@ let newOrder = {
                 }
             })
             .catch((err)=>{
-                controller.createBanner("SOMETHING WENT WRONG, PLEASE REFRESH THE PAGE", "error");
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
             })
             .finally(()=>{
                 loader.style.display = "none";

+ 3 - 3
views/dashboardPage/js/sidebars/newRecipe.js

@@ -1,5 +1,5 @@
 let newRecipe = {
-    display: function(Recipe){
+    display: function(){
         document.getElementById("newRecipeName").value = "";
         document.getElementById("newRecipePrice").value = "";
         document.getElementById("ingredientCount").value = 1;
@@ -14,7 +14,7 @@ let newRecipe = {
         this.changeIngredientCount(categories);
 
         document.getElementById("ingredientCount").onchange = ()=>{this.changeIngredientCount(categories)};
-        document.getElementById("submitNewRecipe").onclick = ()=>{this.submit(Recipe)};
+        document.getElementById("submitNewRecipe").onclick = ()=>{this.submit()};
         document.getElementById("recipeFileUpload").onclick = ()=>{controller.openModal("recipeSpreadsheet")};
     },
 
@@ -62,7 +62,7 @@ let newRecipe = {
         }
     },
 
-    submit: function(Recipe){
+    submit: function(){
         let newRecipe = {
             name: document.getElementById("newRecipeName").value,
             price: document.getElementById("newRecipePrice").value,

+ 5 - 3
views/dashboardPage/js/sidebars/orderFilter.js

@@ -1,5 +1,7 @@
+const Order = require("../classes/Order.js");
+
 let orderFilter = {
-    display: function(Order){
+    display: function(){
         let now = new Date();
         let past = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30);
         let ingredientList = document.getElementById("orderFilterIngredients");
@@ -23,7 +25,7 @@ let orderFilter = {
             element.appendChild(text);
         }
 
-        document.getElementById("orderFilterSubmit").onclick = ()=>{this.submit(Order)};
+        document.getElementById("orderFilterSubmit").onclick = ()=>{this.submit()};
     },
 
     toggleActive: function(element){
@@ -34,7 +36,7 @@ let orderFilter = {
         }
     },
 
-    submit: function(Order){
+    submit: function(){
         let data = {
             from: document.getElementById("orderFilterDateFrom").valueAsDate,
             to: document.getElementById("orderFilterDateTo").valueAsDate,

+ 5 - 3
views/dashboardPage/js/sidebars/transactionFilter.js

@@ -1,5 +1,7 @@
+const Transaction = require("../classes/Transaction.js");
+
 let transactionFilter = {
-    display: function(Transaction){
+    display: function(){
         //Set default dates
         let today = new Date();
         let monthAgo = new Date(today);
@@ -25,7 +27,7 @@ let transactionFilter = {
         }
 
         //Submit button
-        document.getElementById("transFilterSubmit").onclick = ()=>{this.submit(Transaction)};
+        document.getElementById("transFilterSubmit").onclick = ()=>{this.submit()};
     },
 
     toggleActive: function(element){
@@ -36,7 +38,7 @@ let transactionFilter = {
         }
     },
 
-    submit: function(Transaction){
+    submit: function(){
         let data = {
             from: document.getElementById("transFilterDateStart").valueAsDate,
             to: document.getElementById("transFilterDateEnd").valueAsDate,

+ 101 - 0
views/dashboardPage/js/strands/account.js

@@ -0,0 +1,101 @@
+let account = {
+    display: function(){
+        document.getElementById("accountStrandTitle").innerText = merchant.name;
+        document.getElementById("accountEmail").value = merchant.email;
+
+        document.getElementById("accountUpdate").onclick = ()=>{this.updateData()};
+
+        let passButton = document.getElementById("accountShowPassword");
+        let passBox = document.getElementById("changePasswordBox");
+        passButton.onclick = ()=>{
+            passButton.style.display = "none";
+            passBox.style.display = "flex";
+        };
+
+        document.getElementById("cancelPasswordChange").onclick = ()=>{
+            passButton.style.display = "block";
+            passBox.style.display = "none";
+            document.getElementById("accountCurrentPassword").value = "";
+            document.getElementById("accountNewPassword").value = "";
+            document.getElementById("accountConfirmPassword").value = "";
+        };
+
+        document.getElementById("changePasswordButton").onclick = ()=>{this.updatePassword()};
+    },
+
+    updateData: function(){
+        let data = {
+            email: document.getElementById("accountEmail").value
+        }
+
+        let loader = document.getElementById("loaderContainer");
+        loader.style.display = "flex";
+
+        fetch("/merchant/update", {
+            method: "put",
+            headers: {
+                "Content-Type": "application/json;charset=utf-8"
+            },
+            body: JSON.stringify(data)
+        })
+            .then(response => response.json())
+            .then((response)=>{
+                if(typeof(response) === "string"){
+                    controller.createBanner(response, "error");
+                }else{
+                    controller.createBanner("DATA UPDATED", "success");
+                    if(response.email !== merchant.email){
+                        controller.createBanner("YOU MUST VALIDATE YOUR NEW EMAIL ADDRESS BEFORE YOU CAN LOG IN AGAIN", "alert");
+                    }
+
+                    merchant.email = response.email;
+                    document.getElementById("accountEmail").value = merchant.email;
+                }
+            })
+            .catch((err)=>{
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
+            })
+            .finally(()=>{
+                loader.style.display = "none";
+            });
+    },
+
+    updatePassword: function(){
+        let data = {
+            current: document.getElementById("accountCurrentPassword").value,
+            new: document.getElementById("accountNewPassword").value,
+            confirm: document.getElementById("accountConfirmPassword").value
+        }
+        
+        if(data.new !== data.confirm){
+            return controller.createBanner("PASSWORDS DO NOT MATCH");
+        }
+
+        let loader = document.getElementById("loaderContainer");
+        loader.style.display = "flex";
+
+        fetch("/merchant/password", {
+            method: "put",
+            headers: {
+                "Content-Type": "application/json;charset=utf-8"
+            },
+            body: JSON.stringify(data)
+        })
+            .then(response => response.json())
+            .then((response)=>{
+                if(typeof(response) === "string"){
+                    controller.createBanner(response, "error");
+                }else{
+                    window.location.href = response.redirect;
+                }
+            })
+            .catch((err)=>{
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
+            })
+            .finally(()=>{
+                loader.style.display = "none";
+            });
+    }
+}
+
+module.exports = account;

+ 8 - 6
views/dashboardPage/js/strands/analytics.js

@@ -1,10 +1,12 @@
+const Transaction = require("../classes/Transaction.js");
+
 let analytics = {
     isPopulated: false,
     ingredient: undefined,
     recipe: undefined,
     transactionsByDate: [],
 
-    display: function(Transaction){
+    display: function(){
         if(!this.isPopulated){
             document.getElementById("analRecipeContent").style.display = "none";
 
@@ -16,7 +18,7 @@ let analytics = {
             let analSlider = document.getElementById("analSlider");
             analSlider.onclick = ()=>{this.switchDisplay()};
             analSlider.checked = false;
-            document.getElementById("analDateBtn").onclick = ()=>{this.newDates(Transaction)};
+            document.getElementById("analDateBtn").onclick = ()=>{this.newDates()};
 
             this.populateButtons();
 
@@ -27,7 +29,7 @@ let analytics = {
                 this.recipe = merchant.recipes[0];
             }
             
-            this.newDates(Transaction);
+            this.newDates();
             
             this.isPopulated = true;
         }
@@ -68,7 +70,7 @@ let analytics = {
         }
     },
 
-    getData: function(from, to, Transaction){
+    getData: function(from, to){
         let data = {
             from: from,
             to: to,
@@ -297,14 +299,14 @@ let analytics = {
         }
     },
 
-    newDates: async function(Transaction){
+    newDates: async function(){
         const from = document.getElementById("analStartDate").valueAsDate;
         const to = document.getElementById("analEndDate").valueAsDate;
         from.setHours(0, 0, 0, 0);
         to.setDate(to.getDate() + 1);
         to.setHours(0, 0, 0, 0);
 
-        await this.getData(from, to, Transaction);
+        await this.getData(from, to);
 
         if(document.getElementById("analSlider").checked === true){
             this.displayRecipe();

+ 1 - 1
views/dashboardPage/js/strands/home.js

@@ -270,7 +270,7 @@ let home = {
                     }
                 })
                 .catch((err)=>{
-                    controller.createBanner("SOMETHING WENT WRONG.  PLEASE REFRESH THE PAGE", "error");
+                    controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
                 })
                 .finally(()=>{
                     loader.style.display = "none";

+ 6 - 4
views/dashboardPage/js/strands/recipeBook.js

@@ -1,13 +1,15 @@
+const Recipe = require("../classes/Recipe.js");
+
 let recipeBook = {
     isPopulated: false,
     recipeDivList: [],
 
-    display: function(Recipe){
+    display: function(){
         if(!this.isPopulated){
             this.populateRecipes();
 
             if(merchant.pos !== "none"){
-                document.getElementById("posUpdateRecipe").onclick = ()=>{this.posUpdate(Recipe)};
+                document.getElementById("posUpdateRecipe").onclick = ()=>{this.posUpdate()};
             }
             document.getElementById("recipeSearch").oninput = ()=>{this.search()};
 
@@ -61,7 +63,7 @@ let recipeBook = {
         }
     },
 
-    posUpdate: function(Recipe){
+    posUpdate: function(){
         let loader = document.getElementById("loaderContainer");
         loader.style.display = "flex";
         let url = `/recipe/update/${merchant.pos}`;
@@ -103,7 +105,7 @@ let recipeBook = {
                 }
             })
             .catch((err)=>{
-                controller.createBanner("SOMETHING WENT WRONG.  PLEASE REFRESH THE PAGE", "error");
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
             })
             .finally(()=>{
                 loader.style.display = "none";

+ 16 - 12
views/dashboardPage/sidebars.css

@@ -10,7 +10,7 @@
     transition: width 0.3s;
     flex-grow: 0;
     flex-shrink: 0;
-    padding-bottom: 100px;
+    padding-bottom: 25px;
     box-sizing: border-box;
 }
 
@@ -98,19 +98,23 @@
             stroke: black;
         }
 
-.menu > .active{
-    background: rgb(179, 191, 209);
-    cursor: default;
-}
+    .menu a{
+        color: white;
+    }
 
-.menu > .active:hover{
-    background: rgb(179, 191, 209);
-    color: white;
-}
+    .menu > .active{
+        background: rgb(179, 191, 209);
+        cursor: default;
+    }
 
-.menu > .active:hover svg{
-    stroke: white;
-}
+    .menu > .active:hover{
+        background: rgb(179, 191, 209);
+        color: white;
+    }
+
+    .menu > .active:hover svg{
+        stroke: white;
+    }
 
 /* Minimized menu */
 .menuMinimized{

+ 1 - 1
views/otherPages/login.ejs

@@ -24,7 +24,7 @@
             <h1> Welcome Back </h1>
     
             <label>Email
-                <input type="text" name="email" type="email" required>
+                <input type="email" name="email" required>
             </label>
     
             <label>Password

+ 1 - 1
views/otherPages/register.ejs

@@ -28,7 +28,7 @@
             </label>
     
             <label>Email
-                <input type="text" name="email" required>
+                <input type="email" name="email" required>
             </label>
     
             <label>Password

+ 1 - 21
views/otherPages/style.css

@@ -13,19 +13,7 @@ form{
     height: auto;
 }
 
-    input[type=text], select {
-        width: 100%;
-        padding: 12px 20px;
-        margin: 8px 0;
-        display: inline-block;
-        border: 1px solid #ccc;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding-bottom: 16px;
-        font-size: 16px;
-    }
-
-    input[type=text], input[type=password], select {
+    input[type=text], input[type=password], input[type=email], select {
         width: 100%;
         padding: 12px 20px;
         margin: 8px 0;
@@ -58,14 +46,6 @@ form{
         outline: none;
     }
 
-    input[type=button] {
-        background-color: rgb(255, 99, 107);
-    }
-
-    input[type=button]:hover {
-        background-color:rgb(0, 27, 45);
-    }
-
 #logInButton, #joinButton{
     color: white;
 }

+ 1 - 0
views/shared/shared.css

@@ -194,6 +194,7 @@ button::-moz-focus-inner{
     font-weight: bold;
     min-width: 100px;
     margin: 5px;
+    max-height: 52px;
 } 
 
     .buttonWithBorder {