Răsfoiți Sursa

Merge branch 'createOwners' into development

Lee Morgan 5 ani în urmă
părinte
comite
b7f0390c1c
33 a modificat fișierele cu 743 adăugiri și 220 ștergeri
  1. 16 15
      controllers/emailVerification.js
  2. 2 2
      controllers/helper.js
  3. 1 1
      controllers/ingredientData.js
  4. 152 14
      controllers/merchantData.js
  5. 19 14
      controllers/otherData.js
  6. 32 16
      controllers/renderer.js
  7. 40 29
      controllers/squareData.js
  8. 16 13
      middleware.js
  9. 9 28
      models/merchant.js
  10. 45 0
      models/owner.js
  11. 3 0
      routes.js
  12. 87 9
      views/dashboardPage/dashboard.css
  13. 1 0
      views/dashboardPage/dashboard.ejs
  14. 1 1
      views/dashboardPage/ejs/menu.ejs
  15. 47 18
      views/dashboardPage/ejs/strands/account.ejs
  16. 37 51
      views/dashboardPage/js/classes/Merchant.js
  17. 50 3
      views/dashboardPage/js/dashboard.js
  18. 56 0
      views/dashboardPage/js/modal.js
  19. 1 1
      views/dashboardPage/js/sidebars/editIngredient.js
  20. 1 0
      views/dashboardPage/js/sidebars/editRecipe.js
  21. 1 0
      views/dashboardPage/js/sidebars/ingredientDetails.js
  22. 2 0
      views/dashboardPage/js/sidebars/newIngredient.js
  23. 2 0
      views/dashboardPage/js/sidebars/newOrder.js
  24. 2 0
      views/dashboardPage/js/sidebars/newRecipe.js
  25. 2 2
      views/dashboardPage/js/sidebars/newTransaction.js
  26. 1 0
      views/dashboardPage/js/sidebars/orderDetails.js
  27. 1 0
      views/dashboardPage/js/sidebars/recipeDetails.js
  28. 1 1
      views/dashboardPage/js/sidebars/transactionDetails.js
  29. 85 0
      views/dashboardPage/js/strands/account.js
  30. 1 0
      views/dashboardPage/js/strands/home.js
  31. 1 0
      views/dashboardPage/js/strands/orders.js
  32. 28 0
      views/dashboardPage/modal.ejs
  33. 0 2
      views/otherPages/register.ejs

+ 16 - 15
controllers/emailVerification.js

@@ -1,3 +1,4 @@
+const Owner = require("../models/owner.js");
 const Merchant = require("../models/merchant.js");
 
 const mailgun = require("mailgun-js")({apiKey: process.env.MG_SUBLINE_APIKEY, domain: "mail.thesubline.net"});
@@ -5,21 +6,21 @@ const verifyEmail = require("../emails/verifyEmail.js");
 
 module.exports = {
     sendVerifyEmail: function(req, res){
-        Merchant.findOne({_id: req.params.id})
-            .then((merchant)=>{
+        Owner.findOne({_id: req.params.id})
+            .then((owner)=>{
                 const mailgunData = {
                     from: "The Subline <clientsupport@thesubline.net>",
-                    to: merchant.email,
+                    to: owner.email,
                     subject: "Email verification",
                     html: verifyEmail({
-                        name: merchant.name,
-                        link: `${process.env.SITE}/verify/${merchant._id}/${merchant.session.sessionId}`,
+                        name: owner.email,
+                        link: `${process.env.SITE}/verify/${owner._id}/${owner.session.sessionId}`,
                     })
                 };
                 mailgun.messages().send(mailgunData, (err, body)=>{});
 
 
-                return res.render(`verifyPage/verify`, {id: merchant._id, email: merchant.email, banner: res.locals.merchant});
+                return res.render(`verifyPage/verify`, {id: owner._id, email: owner.email, banner: res.locals.merchant});
             })
             .catch((err)=>{
                 req.session.error = "ERROR: UNABLE TO SEND VERIFICATION EMAIL";
@@ -57,27 +58,27 @@ module.exports = {
     },
 
     verify: function(req, res){
-        Merchant.findOne({_id: req.params.id})
-            .then((merchant)=>{
-                if(req.params.code !== merchant.session.sessionId){
+        Owner.findOne({_id: req.params.id})
+            .then((owner)=>{
+                if(req.params.code !== owner.session.sessionId){
                     throw "UNABLE TO VERIFY EMAIL ADDRESS.  INCORRECT LINK";
                 }
 
-                merchant.status.splice(merchant.status.indexOf("unverified"), 1);
+                owner.status.splice(owner.status.indexOf("unverified"), 1);
 
                 const mailgunList = mailgun.lists("clientsupport@mail.thesubline.com");
                 const memberData = {
                     subscribed: true,
-                    address: merchant.email,
-                    name: merchant.name,
+                    address: owner.email,
+                    name: owner.email,
                     vars: {}
                 }
                 mailgunList.members().create(memberData, (err, data)=>{});
 
-                return merchant.save();
+                return owner.save();
             })
-            .then((merchant)=>{
-                req.session.success = "EMAIL VERIFIED.  PLEASE LOG IN";
+            .then((owner)=>{
+                req.session.success = "EMAIL VERIFIED. PLEASE LOG IN";
 
                 return res.redirect("/login");
             })

+ 2 - 2
controllers/helper.js

@@ -3,11 +3,11 @@ const axios = require("axios");
 const Transaction = require("../models/transaction.js");
 
 module.exports = {
-    getSquareData: function(merchant, data){
+    getSquareData: function(owner, merchant, data){
         let ingredients = {};
 
         return axios.post(`${process.env.SQUARE_ADDRESS}/v2/orders/search`, data, {
-            headers: {Authorization: `Bearer ${merchant.square.accessToken}`}
+            headers: {Authorization: `Bearer ${owner.square.accessToken}`}
         })
             .then((response)=>{
                 let transactions = [];

+ 1 - 1
controllers/ingredientData.js

@@ -84,7 +84,7 @@ module.exports = {
                         if(res.locals.merchant.inventory[i].quantity !== req.body.quantity){
                             new InventoryAdjustment({
                                 date: new Date(),
-                                merchant: req.session.user,
+                                merchant: req.session.owner,
                                 ingredient: req.body.id,
                                 quantity: req.body.quantity - res.locals.merchant.inventory[i].quantity
                             }).save().catch(()=>{});

+ 152 - 14
controllers/merchantData.js

@@ -1,5 +1,7 @@
-const Merchant = require("../models/merchant");
+const Owner = require("../models/owner.js");
+const Merchant = require("../models/merchant.js");
 const InventoryAdjustment = require("../models/inventoryAdjustment");
+const Transaction = require("../models/transaction.js");
 
 const helper = require("./helper.js");
 const verifyEmail = require("../emails/verifyEmail.js");
@@ -8,6 +10,48 @@ const bcrypt = require("bcryptjs");
 const mailgun = require("mailgun-js")({apiKey: process.env.MG_SUBLINE_APIKEY, domain: "mail.thesubline.net"});
 
 module.exports = {
+    /*
+    GET: gets a merchant to send back to its owner
+    req.params.id = String (merchant id)
+    response = [Owner, Merchant];
+    */
+    getMerchant: function(req, res){
+        let owner = Owner.findOne({"session.sessionId": req.session.owner}).populate("merchants", "name");
+        let merchant = Merchant.findOne({_id: req.params.id}).populate("inventory.ingredient").populate("recipes");
+        let transactions = Transaction.find({merchant: req.params.id});
+
+        Promise.all([owner, merchant, transactions])
+            .then((response)=>{
+                if(response[0] === null || response[1] === null) throw "unfound";
+                if(response[1].owner.toString() !== response[0]._id.toString()) throw "permissions";
+
+                let responseOwner = {
+                    _id: response[0]._id,
+                    email: response[0].email,
+                    merchants: response[0].merchants
+                };
+
+                for(let i = 0; i < responseOwner.merchants.length; i++){
+                    if(response[1]._id.toString() === responseOwner.merchants[i]._id.toString()){
+                        responseOwner.merchants.splice(i, 1);
+                        break;
+                    }
+                }
+
+                response[1].owner = undefined;
+                response[1].createdAt = undefined;
+
+                req.session.merchant = response[1]._id;
+
+                return res.json([responseOwner, response[1], response[2]]);
+            })
+            .catch((err)=>{
+                if(err === "unfound") return res.json("UNABLE TO FIND THAT MERCHANT");
+                if(err === "permissions") return res.json("YOU DO NOT HAVE PERMISSION TO DO THAT");
+                return res.json("ERROR: UNABLE TO RETRIEVE DATA");
+            });
+    },
+
     /*
     POST - Create a new merchant with no POS system
     req.body = {
@@ -15,6 +59,7 @@ module.exports = {
         email: registration email,
         password: password,
         confirmPassword: confirmation password
+        owner: String (id of the owner, new owner of undefined)
     }
     Redirects to /dashboard
     */
@@ -29,7 +74,8 @@ module.exports = {
             return res.redirect("/register");
         }
 
-        const merchantFind = await Merchant.findOne({email: req.body.email.toLowerCase()});
+        let email = req.body.email.toLowerCase();
+        const merchantFind = await Merchant.findOne({email: email});
         if(merchantFind !== null){
             req.session.error = "USER WITH THIS EMAIL ADDRESS ALREADY EXISTS";
             return res.redirect("/login");
@@ -41,24 +87,32 @@ module.exports = {
         let expirationDate = new Date();
         expirationDate.setDate(expirationDate.getDate() + 90);
 
-        let merchant = new Merchant({
-            name: req.body.name,
-            email: req.body.email.toLowerCase(),
+        let owner = new Owner({
+            email: email,
             password: hash,
-            pos: "none",
-            createdAt: Date.now(),
+            createdAt: new Date(),
             status: ["unverified"],
-            inventory: [],
-            recipes: [],
             session: {
                 sessionId: helper.generateId(25),
                 expiration: expirationDate
-            }
+            },
+            merchants: []
         });
 
-        merchant.save()
-            .then((merchant)=>{
-                return res.redirect(`/verify/email/${merchant._id}`);
+        let merchant = new Merchant({
+            owner: owner._id,
+            name: req.body.name,
+            pos: "none",
+            createdAt: Date.now(),
+            inventory: [],
+            recipes: []
+        });
+
+        owner.merchants.push(merchant._id);
+
+        Promise.all([owner.save(), merchant.save()])
+            .then((response)=>{
+                return res.redirect(`/verify/email/${response[0]._id}`);
             })
             .catch((err)=>{
                 if(typeof(err) === "string"){
@@ -73,6 +127,47 @@ module.exports = {
             });
     },
 
+    /*
+    POST: create new merchant for existing owner
+    req.body = {
+        name: String
+    }
+    response = [Owner, Merchant]
+    */
+    addMerchantNone: function(req, res){
+        let merchant = new Merchant({
+            owner: res.locals.owner._id,
+            name: req.body.name,
+            pos: "none",
+            createdAt: new Date(),
+            inventory: [],
+            recipes: []
+        });
+
+        res.locals.owner.merchants.push(merchant._id);
+
+        let populate = res.locals.owner.populate("merchants", "name").execPopulate();
+
+        Promise.all([res.locals.owner.save(), merchant.save(), populate])
+            .then((response)=>{
+                let owner = {
+                    _id: response[0]._id,
+                    email: response[0].email,
+                    merchants: response[0].merchants
+                }
+
+                req.session.merchant = response[1]._id;
+                
+                return res.json([owner, response[1]]);
+            })
+            .catch((err)=>{
+                if(err.name === "ValidationError"){
+                    return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
+                }
+                return res.json("ERROR: UNABLE TO CREATE NEW MERCHANT");
+            });
+    },
+
     /*
     POST - Update the quantity for a merchant inventory item
     req.body = [{
@@ -98,7 +193,7 @@ module.exports = {
 
                     adjustments.push(new InventoryAdjustment({
                         date: Date.now(),
-                        merchant: req.session.user,
+                        merchant: req.session.owner,
                         ingredient: req.body[i].id,
                         quantity: req.body[i].quantity - updateIngredient.quantity,
                     }));
@@ -250,5 +345,48 @@ module.exports = {
                 return res.json("INCORRECT PASSWORD");
             }
         });
+    },
+
+    /*
+    DELETE: remove a merchant from its owner
+    response = [Owner, Merchant (the next), [Transaction]]
+    */
+    deleteMerchant: function(req, res){
+        if(res.locals.owner.merchants.length === 1) throw "one";
+        for(let i = 0; i < res.locals.owner.merchants.length; i++){
+            if(res.locals.owner.merchants[i].toString() === res.locals.merchant._id.toString()){
+                res.locals.owner.merchants.splice(i, 1);
+                break;
+            }
+        }
+
+        res.locals.merchant.removed = true;
+
+        Promise.all([
+            Merchant.findOne({_id: res.locals.owner.merchants[0]._id}).populate("inventory.ingredient").populate("recipes"),
+            res.locals.owner.save(),
+            res.locals.merchant.save(),
+            res.locals.owner.populate("merchants", "name").execPopulate(),
+            Transaction.find({merchant: res.locals.owner.merchants[0]._id})
+        ])
+            .then((response)=>{
+                let responseOwner = {
+                    _id: res.locals.owner._id,
+                    email: res.locals.owner.email,
+                    merchants: res.locals.owner.merchants.slice(1)
+                };
+
+                response[0].owner = undefined;
+                response[0].createdAt = undefined;
+                response[0].locationId = undefined;
+
+                req.session.merchant = response[0]._id;
+
+                return res.json([responseOwner, response[0], response[4]]);
+            })
+            .catch((err)=>{
+                if(err === "one") return res.json("YOU CANNOT DELETE YOUR ONLY MERCHANT");
+                return res.json("ERROR: UNABLE TO DELETE THE MERCHANT");
+            });
     }
 }

+ 19 - 14
controllers/otherData.js

@@ -1,4 +1,5 @@
-const Merchant = require("../models/merchant");
+const Owner = require("../models/owner.js");
+const Merchant = require("../models/merchant.js");
 const Feedback = require("../models/feedback.js");
 
 const bcrypt = require("bcryptjs");
@@ -13,19 +14,22 @@ module.exports = {
     Redirects to /dashboard
     */
     login: function(req, res){
-        Merchant.findOne({email: req.body.email.toLowerCase()})
-            .then((merchant)=>{
-                if(merchant !== null){
-                    bcrypt.compare(req.body.password, merchant.password, async (err, result)=>{
+        let owner = Owner.findOne({email: req.body.email.toLowerCase()});
+        let merchant = Merchant.findOne({_id: req.session.merchant});
+
+        Promise.all([owner, merchant])
+            .then((response)=>{
+                if(response[0] !== null){
+                    bcrypt.compare(req.body.password, response[0].password, async (err, result)=>{
                         if(result === true){
                             //Check if email has not been verified
-                            if(merchant.status.includes("unverified")){
+                            if(response[0].status.includes("unverified")){
                                 req.session.error = "PLEASE VERIFY YOUR EMAIL ADDRESS";
-                                return res.redirect(`/verify/email/${merchant._id}`);
+                                return res.redirect(`/verify/email/${response[0]._id}`);
                             }
 
                             //Check for suspended account
-                            if(merchant.status.includes("suspended")){
+                            if(response[0].status.includes("suspended")){
                                 req.session.error = "ACCOUNT SUSPENDED. PLEASE CONTACT SUPPORT IF THIS IS IN ERROR";
                                 return res.redirect("/");
                             }
@@ -33,7 +37,7 @@ module.exports = {
                             //Check for out of date access token
                             let cutoff = new Date();
                             cutoff.setDate(cutoff.getDate() + 1);
-                            if(merchant.pos === "square" && merchant.square.expires < cutoff){
+                            if(response[0].square !== undefined && response[0].square.expires < cutoff){
                                 let data = await axios.post(`${process.env.SQUARE_ADDRESS}/oauth2/token`, {
                                     client_id: process.env.SUBLINE_SQUARE_APPID,
                                     client_secret: process.env.SUBLINE_SQUARE_APPSECRET,
@@ -41,13 +45,14 @@ module.exports = {
                                     refresh_token: merchant.square.refreshToken
                                 });
 
-                                merchant.square.accessToken = data.data.access_token;
-                                merchant.square.expires = new Date(data.data.expires_at);
+                                response[0].square.accessToken = data.data.access_token;
+                                response[0].square.expires = new Date(data.data.expires_at);
 
-                                await merchant.save();
+                                await response[0].save();
                             }
 
-                            req.session.user = merchant.session.sessionId;
+                            req.session.merchant = (response[1] === null) ? await Merchant.findOne({_id: response[0].merchants[0]}) : response[1];
+                            req.session.owner = response[0].session.sessionId;
                             return res.redirect("/dashboard");
                         }else{
                             req.session.error = "INVALID EMAIL OR PASSWORD";
@@ -71,7 +76,7 @@ module.exports = {
     Redirects to /
     */
     logout: function(req, res){
-        req.session.user = undefined;
+        req.session.owner = undefined;
 
         return res.redirect("/");
     },

+ 32 - 16
controllers/renderer.js

@@ -14,7 +14,7 @@ module.exports = {
 
     //GET: Renders the login page
     loginPage: function(req, res){
-        if(req.session.user !== undefined) return res.redirect("/dashboard");
+        if(req.session.owner !== undefined) return res.redirect("/dashboard");
         return res.render("otherPages/login", {banner: res.locals.banner});
     },
 
@@ -29,9 +29,9 @@ module.exports = {
     Renders inventoryPage
     */
     displayDashboard: function(req, res){
-        if(res.locals.merchant.status.includes("unverified")) {
+        if(res.locals.owner.status.includes("unverified")) {
             req.session.error = "PLEASE VERIFY YOUR EMAIL ADDRESS";
-            return res.redirect(`/verify/email/${res.locals.merchant._id}`);
+            return res.redirect(`/verify/email/${res.locals.owner._id}`);
         }
 
         res.locals.merchant
@@ -42,9 +42,9 @@ module.exports = {
                 let date = new Date();
                 let firstDay = new Date(date.getFullYear(), date.getMonth() - 1, 1);
 
-                return Transaction.aggregate([
+                let transactions = Transaction.aggregate([
                     {$match: {
-                        merchant: new ObjectId(res.locals.merchant._id),
+                        merchant: new ObjectId(merchant._id),
                         date: {$gte: firstDay},
                     }},
                     {$sort: {date: -1}},
@@ -52,10 +52,16 @@ module.exports = {
                         date: 1,
                         recipes: 1
                     }}
-                ]);      
+                ]);
+                
+                let merchants = res.locals.owner.populate("merchants", "name").execPopulate();
+
+                return Promise.all([transactions, merchants]);
             })
-            .then(async (transactions)=>{
-                if(res.locals.pos !== "none"){
+            .then(async (response)=>{
+                let transactions = response[0];
+
+                if(res.locals.merchant.pos !== "none"){
                     let latest = null;
                     if(transactions.length === 0){
                         let latestTransaction = await Transaction.find({merchant: res.locals.merchant._id}).sort({date: -1}).limit(1);
@@ -69,7 +75,7 @@ module.exports = {
                         let now = new Date();
 
                         let postData = {
-                            location_ids: [res.locals.merchant.square.location],
+                            location_ids: [res.locals.merchant.locationId],
                             query: {
                                 filter: {
                                     date_time_filter: {
@@ -91,7 +97,7 @@ module.exports = {
                         };
 
                         do{
-                            let newOrders = await helper.getSquareData(res.locals.merchant, postData);
+                            let newOrders = await helper.getSquareData(res.locals.owner, res.locals.merchant, postData);
                             postData.cursor = newOrders.cursor;
                             for(let i = 0; i < newOrders.length; i++){
                                 for(let j = 0; j < newOrders[i].recipes.length; j++){
@@ -103,13 +109,23 @@ module.exports = {
                     }
                 }
 
-                res.locals.merchant._id = undefined;
-                res.locals.password = undefined;
-                res.locals.merchant.status = undefined;
-                res.locals.square = undefined;
-                res.locals.session = undefined;
+                res.locals.merchant.owner = undefined;
+                res.locals.createdAt = undefined;
+                
+                res.locals.owner.password = undefined;
+                res.locals.owner.status = undefined;
+                res.locals.owner.square = undefined;
+                res.locals.owner.createdAt = undefined;
+                res.locals.owner.session = undefined;
+
+                for(let i = 0; i < res.locals.owner.merchants.length; i++){
+                    if(res.locals.owner.merchants[i]._id.toString() === res.locals.merchant._id.toString()){
+                        res.locals.owner.merchants.splice(i, 1);
+                        break;
+                    } 
+                }
 
-                return res.render("dashboardPage/dashboard", {merchant: res.locals.merchant, transactions: transactions});
+                return res.render("dashboardPage/dashboard", {owner: res.locals.owner, merchant: res.locals.merchant, transactions: transactions});
             })
             .catch((err)=>{
                 req.session.error = "ERROR: UNABLE TO RETRIEVE DATA";

+ 40 - 29
controllers/squareData.js

@@ -1,3 +1,4 @@
+const Owner = require("../models/owner.js");
 const Merchant = require("../models/merchant.js");
 const Recipe = require("../models/recipe.js");
 const Transaction = require("../models/transaction.js");
@@ -23,8 +24,8 @@ module.exports = {
         }
         let email = req.body.email.toLowerCase();
 
-        let potentialMerchant = await Merchant.findOne({email: email})
-        if(potentialMerchant !== null){
+        let potentialOwner = await Owner.findOne({email: email})
+        if(potentialOwner !== null){
             req.session.error = "USER WITH THIS EMAIL ADDRESS ALREADY EXISTS";
             return res.redirect("/login");
         }
@@ -35,25 +36,34 @@ module.exports = {
         let salt = bcrypt.genSaltSync(10);
         let hash = bcrypt.hashSync(req.body.password, salt);
 
-        let merchant = new Merchant({
-            name: req.body.name,
+        let owner = new Owner({
             email: email,
             password: hash,
-            pos: "square",
-            status: ["unverified"],
-            inventory: [],
-            recipes: [],
             square: {},
             createdAt: new Date(),
+            status: ["unverified"],
             session: {
                 sessionId: helper.generateId(25),
                 expiration: expirationDate
-            }
+            },
+            merchants: []
         });
 
-        merchant.save()
+        let merchant = new Merchant({
+            owner: owner._id,
+            name: req.body.name,
+            pos: "square",
+            inventory: [],
+            recipes: [],
+            square: {},
+            createdAt: new Date()
+        });
+
+        owner.merchants.push(merchant._id);
+
+        Promise.all([owner.save(), merchant.save()])
             .then((response)=>{
-                req.session.user = merchant.session.sessionId;
+                req.session.owner = response[0].session.sessionId;
                 return res.redirect(`${process.env.SQUARE_ADDRESS}/oauth2/authorize?client_id=${process.env.SUBLINE_SQUARE_APPID}&scope=INVENTORY_READ+ITEMS_READ+MERCHANT_PROFILE_READ+ORDERS_READ+PAYMENTS_READ`);
             })
             .catch((err)=>{
@@ -76,47 +86,47 @@ module.exports = {
         }
 
         let merchant = {};
-
-        let localMerchant = Merchant.findOne({"session.sessionId": req.session.user});
+        let owner = Owner.findOne({"session.sessionId": req.session.owner}).populate("merchants");
         let squareMerchant = axios.post(url, data);
-        Promise.all([localMerchant, squareMerchant])
+        Promise.all([owner, squareMerchant])
             .then((response)=>{
                 if(response[0] === null) throw "ERROR: UNABLE TO CREATE ACCOUNT";
 
-                merchant = response[0];
+                owner = response[0];
+                merchant = response[0].merchants[0];
 
-                merchant.square = {
+                owner.square = {
                     id: response[1].data.merchant_id,
                     expires: new Date(response[1].data.expires_at),
                     refreshToken: response[1].data.refresh_token,
                     accessToken: response[1].data.access_token
                 };
 
-                return axios.get(`${process.env.SQUARE_ADDRESS}/v2/merchants/${merchant.square.id}`, {
-                    headers: {Authorization: `Bearer ${merchant.square.accessToken}`}
+                return axios.get(`${process.env.SQUARE_ADDRESS}/v2/merchants/${owner.square.id}`, {
+                    headers: {Authorization: `Bearer ${owner.square.accessToken}`}
                 });
             })    
             .then((response)=>{
-                merchant.square.location = response.data.merchant.main_location_id;
+                merchant.locationId = response.data.merchant.main_location_id;
 
                 let items = axios.post(`${process.env.SQUARE_ADDRESS}/v2/catalog/search`, {
                     object_types: ["ITEM"]
                 }, {
                     headers: {
-                        Authorization: `Bearer ${merchant.square.accessToken}`
+                        Authorization: `Bearer ${owner.square.accessToken}`
                     }
                 });
 
-                let location = axios.get(`${process.env.SQUARE_ADDRESS}/v2/locations/${merchant.square.location}`, {
+                let location = axios.get(`${process.env.SQUARE_ADDRESS}/v2/locations/${merchant.locationId}`, {
                     headers: {
-                        Authorization: `Bearer ${merchant.square.accessToken}`
+                        Authorization: `Bearer ${owner.square.accessToken}`
                     }
                 });
 
                 return Promise.all([items, location]);
             })
             .then((response)=>{
-                if(merchant.email === response[1].data.location.business_email) merchant.status = [];
+                if(owner.email === response[1].data.location.business_email) merchant.status = [];
                 let recipes = [];
                 
                 for(let i = 0; i < response[0].data.objects.length; i++){
@@ -149,21 +159,22 @@ module.exports = {
                     }
                 }
     
-                return Promise.all([Recipe.create(recipes), merchant.save()]);
+                return Promise.all([Recipe.create(recipes), owner.save(), merchant.save()]);
             })
             .then((response)=>{
-                req.session.user = response[1].session.sessionId;
+                req.session.owner = response[1].session.sessionId;
+                req.session.merchant = merchant._id;
     
                 res.redirect("/dashboard");
 
                 let body = {
-                    location_ids: [merchant.square.location],
+                    location_ids: [merchant.locationId],
                     limit: 10000,
                     query: {}
                 };
                 let options = {
                     headers: {
-                        Authorization: `Bearer ${merchant.square.accessToken}`,
+                        Authorization: `Bearer ${owner.square.accessToken}`,
                         "Content-Type": "application/json"
                     }
                 };
@@ -198,14 +209,14 @@ module.exports = {
                 }
 
                 let body = {
-                    location_ids: [merchant.square.location],
+                    location_ids: [merchant.locationId],
                     limit: 10000,
                     cursor: response.data.cursor,
                     query: {}
                 };
                 let options = {
                     headers: {
-                        Authorization: `Bearer ${merchant.square.accessToken}`,
+                        Authorization: `Bearer ${owner.square.accessToken}`,
                         "Content-Type": "application/json"
                     }
                 };

+ 16 - 13
middleware.js

@@ -1,37 +1,40 @@
-const Merchant = require("./models/merchant.js")
+const Owner = require("./models/owner.js");
+const Merchant = require("./models/merchant.js");
 
 const helper = require("./controllers/helper.js");
 
 module.exports = {
     verifySession: function(req, res, next){
-        if(req.session.user === undefined) {
+        if(req.session.owner === undefined) {
             req.session.error = "PLEASE LOG IN";
             return res.redirect("/login");
         }
     
-        Merchant.findOne({"session.sessionId": req.session.user})
-            .then((merchant)=>{
-                if(merchant === null){
-                    throw "login";
-                }
+        let owner = Owner.findOne({"session.sessionId": req.session.owner});
+        let merchant = Merchant.findOne({_id: req.session.merchant});
+        Promise.all([owner, merchant])
+            .then((response)=>{
+                if(response[0] === null || response[1] === null) throw "login";
+                if(response[0]._id.toString() !== response[1].owner.toString()) throw "login";
     
                 //Check if session is out of date
-                if(merchant.session.date < new Date()){
+                if(response[0].session.expiration < new Date()){
                     let newExpiration = new Date();
                     newExpiration.setDate(newExpiration.getDate() + 90);
     
-                    merchant.session.sessionId = helper.generateId(25);
-                    merchant.session.date = newExpiration;
-                    merchant.save();
+                    response[0].session.sessionId = helper.generateId(25);
+                    response[0].session.expiration = newExpiration;
+                    response[0].save();
                     throw "login";
                 }
 
-                res.locals.merchant = merchant;
+                res.locals.owner = response[0];
+                res.locals.merchant = response[1];
                 return next();
             })
             .catch((err)=>{
                 if(err === "login"){
-                    req.session.user = undefined;
+                    req.session.owner = undefined;
                     req.session.error = "PLEASE LOG IN";
                     return res.redirect("/login");
                 }

+ 9 - 28
models/merchant.js

@@ -2,11 +2,12 @@ const isSanitary = require("../controllers/helper.js").isSanitary;
 
 const mongoose = require("mongoose");
 
-let emailValid = (value)=>{
-    return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value);
-}
-
 const MerchantSchema = new mongoose.Schema({
+    owner: {
+        type: mongoose.Schema.Types.ObjectId,
+        ref: "Owner",
+        required: true
+    },
     name: {
         type: String,
         required: [true, "MERCHANT NAME IS REQUIRED"],
@@ -15,32 +16,15 @@ const MerchantSchema = new mongoose.Schema({
             message: "NAME CONTAINS ILLEGAL CHARACTERS"
         }
     },
-    email: {
-        type: String,
-        required: [true, "EMAIL IS REQUIRED"],
-        validate: {
-            validator: emailValid,
-            message: "INVALID EMAIL ADDRESS"
-        },
-        index: true
-    },
-    password: String,
     pos: {
         type: String,
         required: true
     },
-    square: {
-        id: String,
-        accessToken: String,
-        expires: Date,
-        refreshToken: String,
-        location: String
-    },
+    locationId: String,
     createdAt: {
         type: Date,
         default: new Date()
     },
-    status: [],
     inventory: [{
         ingredient: {
             type: mongoose.Schema.Types.ObjectId,
@@ -60,12 +44,9 @@ const MerchantSchema = new mongoose.Schema({
         type: mongoose.Schema.Types.ObjectId,
         ref: "Recipe"
     }],
-    session: {
-        sessionId: {
-            type: String,
-            index: true
-        },
-        expiration: Date
+    removed: {
+        type: Boolean,
+        default: false
     }
 });
 

+ 45 - 0
models/owner.js

@@ -0,0 +1,45 @@
+const mongoose = require("mongoose");
+
+let emailValid = (value)=>{
+    return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value);
+}
+
+const OwnerSchema = new mongoose.Schema({
+    email: {
+        type: String,
+        required: [true, "EMAIL IS REQUIRED"],
+        validate: {
+            validator: emailValid,
+            message: "INVALID EMAIL ADDRESS"
+        },
+        index: true
+    },
+    password: {
+        type: String,
+        required: true
+    },
+    square: {
+        id: String,
+        accessToken: String,
+        expires: Date,
+        refreshToken: String,
+    },
+    createdAt: {
+        type: Date,
+        default: new Date()
+    },
+    status: [],
+    session: {
+        sessionId: {
+            type: String,
+            index: true
+        },
+        expiration: Date
+    },
+    merchants: [{
+        type: mongoose.Schema.Types.ObjectId,
+        ref: "Merchant"
+    }]
+});
+
+module.exports = mongoose.model("Owner", OwnerSchema);

+ 3 - 0
routes.js

@@ -24,11 +24,14 @@ module.exports = function(app){
     app.get("/resetpassword/*", renderer.displayPassReset);
     
     //Merchant
+    app.get("/merchant/:id", merchantData.getMerchant);
     app.post("/merchant/create/none", merchantData.createMerchantNone);
+    app.post("/merchant/add/none", session, merchantData.addMerchantNone);
     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);
+    app.delete("/merchant", session, merchantData.deleteMerchant);
 
     //Ingredients
     app.post("/ingredients/create", session, ingredientData.createIngredient);  //also adds to merchant

+ 87 - 9
views/dashboardPage/dashboard.css

@@ -573,22 +573,60 @@ ACCOUNT STRAND
 #accountStrand{
     padding: 15px;
     box-sizing: border-box;
+    align-items: center;
 }
 
-    .horizontal{
-        display: flex;
-        justify-content: space-around;
-    }
-
-    .vertical{
+    .settings{
         display: flex;
         flex-direction: column;
+        width: 75%;
+        max-height: 100%;
+        overflow-y: auto;
     }
 
+        .settingsSection{
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            margin: 15px 0;
+            background: rgb(201, 201, 201);
+            padding: 35px;
+        }
+
+            .settingsSectionHeader{
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                margin-bottom: 25px;
+                border-bottom: 1px solid black;
+            }
+
     #accountUpdate{
         max-width: 500px;
     }
 
+    #settingsMerchants{
+        width: 85%;
+    }
+
+        .locationDiv{
+            width: 100%;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 0 25px;
+            background: rgb(240, 252, 255);
+            box-shadow: 0 0 10px black;
+            margin: 10px 0;
+        }
+
+            .locationDiv p{
+                font-size: 25px;
+                font-weight: bold;
+            }
+
     #accountShowPassword{
         max-width: 300px;
     }
@@ -596,8 +634,8 @@ ACCOUNT STRAND
     #changePasswordBox{
         flex-direction: column;
         padding: 25px;
-        background: rgb(240, 252, 255);
-        border: 2px solid black;
+        background: rgb(201, 201, 201);
+        /* border: 2px solid black; */
         border-radius: 10px;
     }
 
@@ -635,7 +673,7 @@ Modal
         align-items: flex-end;
     }
 
-        .modalBox svg{
+        .modalBox > svg{
             color: rgb(255, 99, 107);
             cursor: pointer;
         }
@@ -672,6 +710,46 @@ Modal
 
         }
 
+    #modalNewMerchant{
+        display: flex;
+        flex-direction: column;
+        background: white;
+        padding: 50px;
+        min-width: 350px;
+    }
+
+        #modalNewMerchant h2{
+            text-align: center;
+        }
+
+        #modalNewMerchant > *{
+            margin: 15px;
+        }
+
+        #modalNewMerchant label{
+            display: flex;
+            flex-direction: column;
+        }
+
+    #modalConfirm{
+        display: flex;
+        flex-direction:column;
+        align-items: center;
+        background: white;
+        padding: 50px;
+    }
+
+        #modalConfirm svg{
+            font-size: 50px;
+            margin: 15px;
+            font-weight: bold;
+        }
+
+        #modalConfirm p{
+            font-size: 25px;
+            font-weight: bold;
+        }
+
 @media screen and (orientation: portrait){
     @media screen and (max-width: 1400px){
         body{

+ 1 - 0
views/dashboardPage/dashboard.ejs

@@ -55,6 +55,7 @@
 
         <script>
             let data = {
+                owner: <%- JSON.stringify(owner) %>,
                 merchant: <%- JSON.stringify(merchant) %>,
                 transactions: <%- JSON.stringify(transactions) %>
             }

+ 1 - 1
views/dashboardPage/ejs/menu.ejs

@@ -71,7 +71,7 @@
             <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>
+        <p>SETTINGS</p>
     </button>
 
     <a class="menuButton" href="/logout">

+ 47 - 18
views/dashboardPage/ejs/strands/account.ejs

@@ -3,8 +3,11 @@
         <h1 id="accountStrandTitle" class="strandTitle"></h1>
     </div>
 
-    <div class="horizontal">
-        <div class="vertical">
+    <div class="settings">
+        <div class="settingsSection">
+            <div class="settingsSectionHeader">
+                <h2>ACCOUNT INFORMATION</h2>
+            </div>
             <label>EMAIL
                 <input id="accountEmail" type="email">
             </label>
@@ -12,28 +15,54 @@
             <button id="accountUpdate" class="button">UPDATE</button>
         </div>
 
-        <button id="accountShowPassword" class="button">CHANGE PASSWORD</button>
+        <div class="settingsSection">
+            <div class="settingsSectionHeader">
+                <h2>YOUR LOCATIONS</h2>
 
-        <div id="changePasswordBox" style="display:none;">
-            <label>CURRENT PASSWORD
-                <input id="accountCurrentPassword" type="password">
-            </label>
+                <button id="settingsAddMerchant" class="button">NEW LOCATION</button>
+            </div>
 
-            <label>NEW PASSWORD
-                <input id="accountNewPassword" type="password">
-            </label>
+            <div id="settingsMerchants"></div>
+        </div>
 
-            <label>CONFIRM NEW PASSWORD
-                <input id="accountConfirmPassword" type="password">
-            </label>
+        <div class="settingsSection">
+            <div class="settingsSectionHeader">
+                <h2>SECURITY</h2>
+            </div>
 
-            <div id="accountButtonBox" class="buttonBox">
-                <button id="cancelPasswordChange" class="button">CANCEL</button>
+            <button id="deleteMerchant" class="button">DELETE THIS MERCHANT</button>
 
-                <button id="changePasswordButton" class="button">CHANGE PASSWORD</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>
 
-            <p>* This will log you out of all current sessions</p>
+                <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>
+
+        <template id="locationDiv">
+            <div class="locationDiv">
+                <p></p>
+
+                <button class="button">USE</button>
+            </div>
+        </template>
     </div>
 </div>

+ 37 - 51
views/dashboardPage/js/classes/Merchant.js

@@ -3,12 +3,6 @@ const Recipe = require("./Recipe.js");
 const Transaction = require("./Transaction.js");
 const Order = require("./Order.js");
 
-const homeStrand = require("../strands/home.js");
-const ingredientsStrand = require("../strands/ingredients.js");
-const recipeBookStrand = require("../strands/recipeBook");
-const analyticsStrand = require("../strands/analytics.js");
-const ordersStrand = require("../strands/orders");
-
 class MerchantIngredient{
     constructor(ingredient, quantity){
         this._quantity = quantity;
@@ -77,40 +71,53 @@ class MerchantIngredient{
 }
 
 class Merchant{
-    constructor(oldMerchant, transactions){
-        this._name = oldMerchant.name;
-        this._email = oldMerchant.email;
-        this._pos = oldMerchant.pos;
+        constructor(
+            name,
+            email,
+            pos,
+            ingredients,
+            recipes,
+            transactions,
+            owner
+        ){
+        this._name = name;
+        this._email = email;
+        this._pos = pos;
         this._ingredients = [];
         this._recipes = [];
         this._transactions = [];
         this._orders = [];
+        this._owner = {
+            id: owner._id,
+            email: owner.email,
+            merchants: owner.merchants
+        };
         
         //populate ingredients
-        for(let i = 0; i < oldMerchant.inventory.length; i++){
+        for(let i = 0; i < ingredients.length; i++){
             const ingredient = new Ingredient(
-                oldMerchant.inventory[i].ingredient._id,
-                oldMerchant.inventory[i].ingredient.name,
-                oldMerchant.inventory[i].ingredient.category,
-                oldMerchant.inventory[i].ingredient.unitType,
-                oldMerchant.inventory[i].defaultUnit,
+                ingredients[i].ingredient._id,
+                ingredients[i].ingredient.name,
+                ingredients[i].ingredient.category,
+                ingredients[i].ingredient.unitType,
+                ingredients[i].defaultUnit,
                 this,
-                oldMerchant.inventory[i].ingredient.unitSize
+                ingredients[i].ingredient.unitSize
             );
 
             const merchantIngredient = new MerchantIngredient(
                 ingredient,
-                oldMerchant.inventory[i].quantity,
+                ingredients[i].quantity,
             );
 
             this._ingredients.push(merchantIngredient);
         }
 
         //populate recipes
-        for(let i = 0; i < oldMerchant.recipes.length; i++){
+        for(let i = 0; i < recipes.length; i++){
             let ingredients = [];
-            for(let j = 0; j < oldMerchant.recipes[i].ingredients.length; j++){
-                const ingredient = oldMerchant.recipes[i].ingredients[j];
+            for(let j = 0; j < recipes[i].ingredients.length; j++){
+                const ingredient = recipes[i].ingredients[j];
                 for(let k = 0; k < this._ingredients.length; k++){
                     if(ingredient.ingredient === this._ingredients[k].ingredient.id){
                         ingredients.push({
@@ -123,9 +130,9 @@ class Merchant{
             }
 
             this._recipes.push(new Recipe(
-                oldMerchant.recipes[i]._id,
-                oldMerchant.recipes[i].name,
-                oldMerchant.recipes[i].price,
+                recipes[i]._id,
+                recipes[i].name,
+                recipes[i].price,
                 ingredients,
                 this
             ));
@@ -199,9 +206,6 @@ class Merchant{
             const merchantIngredient = new MerchantIngredient(createdIngredient, quantity);
             this._ingredients.push(merchantIngredient);
         }
-
-        ingredientsStrand.populateByProperty();
-        analyticsStrand.populateButtons();
     }
 
     removeIngredient(ingredient){
@@ -211,10 +215,6 @@ class Merchant{
         }
 
         this._ingredients.splice(index, 1);
-
-        homeStrand.drawInventoryCheckCard();
-        ingredientsStrand.populateByProperty();
-        analyticsStrand.populateButtons();
     }
 
     getIngredient(id){
@@ -258,9 +258,6 @@ class Merchant{
                 this
             ));
         }
-
-        recipeBookStrand.populateRecipes();
-        analyticsStrand.populateButtons();
     }
 
     removeRecipe(recipe){
@@ -271,8 +268,7 @@ class Merchant{
 
         this._recipes.splice(index, 1);
 
-        recipeBookStrand.populateRecipes();
-        analyticsStrand.populateButtons();
+        state.updateRecipes();
     }
 
     get transactions(){
@@ -328,11 +324,6 @@ class Merchant{
         }
 
         this.transactions.sort((a, b) => (a.date > b.date) ? 1 : -1);
-
-        homeStrand.isPopulated = false;
-        ingredientsStrand.populateByProperty();
-        analyticsStrand.displayIngredient();
-        analyticsStrand.displayRecipe();
     }
 
     removeTransaction(transaction){
@@ -348,10 +339,7 @@ class Merchant{
 
         this._transactions.splice(this._transactions.indexOf(transaction), 1);
 
-        homeStrand.isPopulated = false;
-        ingredientsStrand.populateByProperty();
-        analyticsStrand.displayIngredient();
-        analyticsStrand.displayRecipe();
+        state.updateTransactions();
     }
 
     get orders(){
@@ -396,9 +384,6 @@ class Merchant{
                 }
             }
         }
-
-        ingredientsStrand.populateByProperty();
-        ordersStrand.displayOrders();
     }
 
     removeOrder(order){
@@ -417,15 +402,16 @@ class Merchant{
                 }
             }
         }
-
-        ingredientsStrand.isPopulated = false;
-        ordersStrand.isPopulated = false;
     }
 
     get units(){
         return this._units;
     }
 
+    get owner(){
+        return this._owner;
+    }
+
     getRevenue(from, to = new Date()){
         const {start, end} = this.getTransactionIndices(from, to);
 

+ 50 - 3
views/dashboardPage/js/dashboard.js

@@ -23,7 +23,15 @@ const modalScript = require("./modal.js");
 
 const Merchant = require("./classes/Merchant.js");
 
-merchant = new Merchant(data.merchant, data.transactions);
+window.merchant = new Merchant(
+    data.merchant.name,
+    data.owner.email,
+    data.merchant.pos,
+    data.merchant.inventory,
+    data.merchant.recipes,
+    data.transactions,
+    data.owner
+);
 
 controller = {
     openStrand: function(strand, data = undefined){
@@ -220,6 +228,16 @@ controller = {
             case "feedback":
                 modalScript.feedback();
                 break;
+            case "newMerchant":
+                modalScript.newMerchant();
+                break;
+            case "confirmDeleteMerchant":
+                let div = document.getElementById("modalConfirm");
+                div.style.display = "flex";
+                div.children[1].innerText = "Are you sure you want to delete this merchant?";
+                div.children[2].children[1].onclick = ()=>{account.deleteMerchant()};
+                div.children[2].children[0].onclick = ()=>{controller.closeModal()};
+                break;
         }
     },
 
@@ -305,10 +323,39 @@ controller = {
         document.getElementById("menu").style.display = "none";
         document.querySelector(".contentBlock").style.display = "flex";
         document.getElementById("mobileMenuSelector").onclick = ()=>{this.openMenu()};
+    }
+}
+
+window.state = {
+    updateIngredients: function(){
+        ingredients.populateByProperty();
+        analytics.populateButtons();
+        home.drawInventoryCheckCard();
+    },
+
+    updateRecipes: function(){
+        recipeBook.populateRecipes();
+        analytics.populateButtons();
+    },
+
+    updateTransactions: function(){
+        home.isPopulated = false;
+        ingredients.populateByProperty();
+        analytics.displayIngredient();
+        analytics.displayRecipe();
+        home.drawRevenueGraph();
+    },
+
+    updateOrders: function(){
+        ingredients.isPopulated = false;
+        ordersStrand.isPopulated = false;
     },
 
-    updateAnalytics: function(){
-        analytics.isPopulated = false;
+    updateMerchant(){
+        this.updateIngredients();
+        this.updateRecipes();
+        this.updateTransactions();
+        this.updateOrders();
     }
 }
 

+ 56 - 0
views/dashboardPage/js/modal.js

@@ -1,3 +1,6 @@
+const merchant = require("../../../models/merchant.js");
+const Merchant = require("./classes/Merchant.js");
+
 let modal = {
     feedback: function(){
         let form = document.getElementById("modalFeedback");
@@ -38,6 +41,59 @@ let modal = {
             .finally(()=>{
                 loader.style.display = "none";
             });
+    },
+
+    newMerchant: function(){
+        let form = document.getElementById("modalNewMerchant");
+        form.style.display = "flex";
+        form.onsubmit = ()=>{this.submitNewMerchantNone()};
+    },
+
+    submitNewMerchantNone(){
+        event.preventDefault();
+
+        let data = {
+            name: document.getElementById("addMerchantName").value,
+        };
+
+        let loader = document.getElementById("loaderContainer");
+        loader.style.display = "flex";
+
+        fetch("/merchant/add/none", {
+            method: "post",
+            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{
+                    let newMerchant = new Merchant(
+                        response[1].name,
+                        response[0].email,
+                        response[1].pos,
+                        response[1].inventory,
+                        response[1].recipes,
+                        [],
+                        response[0]
+                    );
+
+                    window.merchant = newMerchant;
+
+                    state.updateMerchant();
+                    controller.openStrand("home");
+                    controller.closeModal();
+                }
+            })
+            .catch((err)=>{
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
+            })
+            .finally(()=>{
+                loader.style.display = "none";
+            });
     }
 };
 

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

@@ -105,7 +105,7 @@ let editIngredient = {
             }else{
                 merchant.removeIngredient(merchant.getIngredient(response.ingredient._id));
                 merchant.addIngredients([response]);
-
+                state.updateIngredients();
                 controller.openStrand("ingredients");
                 controller.createBanner("INGREDIENT UPDATED", "success");
             }

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

@@ -115,6 +115,7 @@ let editRecipe = {
                 }else{
                     merchant.removeRecipe(recipe)
                     merchant.addRecipes([response]);
+                    state.updateRecipes();
                     controller.openStrand("recipeBook");
                     controller.createBanner("RECIPE UPDATED", "success");
                 }

+ 1 - 0
views/dashboardPage/js/sidebars/ingredientDetails.js

@@ -71,6 +71,7 @@ let ingredientDetails = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.removeIngredient(ingredient);
+                    state.updateIngredients();
                     
                     controller.openStrand("ingredients");
                     controller.createBanner("INGREDIENT REMOVED", "success");

+ 2 - 0
views/dashboardPage/js/sidebars/newIngredient.js

@@ -62,6 +62,7 @@ let newIngredient = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.addIngredients([response]);
+                    state.updateIngredients();
                     controller.openStrand("ingredients");
 
                     controller.createBanner("INGREDIENT CREATED", "success");
@@ -96,6 +97,7 @@ let newIngredient = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.addIngredients(response);
+                    state.updateIngredients();
 
                     controller.createBanner("INGREDIENTS SUCCESSFULLY ADDED", "success");
                     controller.openStrand("ingredients");

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

@@ -91,6 +91,7 @@ let newOrder = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.addOrders([response], true);
+                    state.updateOrders();
                     
                     controller.openStrand("orders", merchant.orders);
                     controller.createBanner("NEW ORDER CREATED", "success");
@@ -150,6 +151,7 @@ let newOrder = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.addOrders([response], true);
+                    state.updateOrders();
 
                     controller.createBanner("ORDER CREATED AND INGREDIENTS UPDATED SUCCESSFULLY", "success");
                     controller.openStrand("orders");

+ 2 - 0
views/dashboardPage/js/sidebars/newRecipe.js

@@ -98,6 +98,7 @@ let newRecipe = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.addRecipes([response]);
+                    state.updateRecipes();
 
                     controller.createBanner("RECIPE CREATED", "success");
                     controller.openStrand("recipeBook");
@@ -135,6 +136,7 @@ let newRecipe = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.addRecipes(response);
+                    state.updateRecipes();
 
                     controller.createBanner("ALL RECIPES SUCCESSFULLY CREATED", "success");
                     controller.openStrand("recipeBook");

+ 2 - 2
views/dashboardPage/js/sidebars/newTransaction.js

@@ -76,8 +76,8 @@ let newTransaction = {
                         controller.createBanner(response, "error");
                     }else{
                         merchant.addTransactions([response], true);
+                        state.updateTransactions();
 
-                        controller.updateAnalytics();
                         controller.openStrand("transactions", merchant.getTransactions());
                         controller.createBanner("TRANSACTION CREATED", "success");
                     }
@@ -116,7 +116,7 @@ let newTransaction = {
                         response.recipes[i].recipe = response.recipes[i].recipe._id;
                     }
                     merchant.addTransactions([response], true);
-                    controller.updateAnalytics();
+                    state.updateTransactions();
 
                     controller.openStrand("transactions", merchant.transactions);
                     controller.createBanner("TRANSACTION SUCCESSFULLY CREATED.  INGREDIENTS UPDATED", "success");

+ 1 - 0
views/dashboardPage/js/sidebars/orderDetails.js

@@ -50,6 +50,7 @@ let orderDetails = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.removeOrder(order);
+                    state.updateOrders();
 
                     controller.openStrand("orders", merchant.orders);
                     controller.createBanner("ORDER REMOVED", "success");

+ 1 - 0
views/dashboardPage/js/sidebars/recipeDetails.js

@@ -41,6 +41,7 @@ let recipeDetails = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.removeRecipe(recipe);
+                    state.updateRecipes();
 
                     controller.createBanner("RECIPE REMOVED", "success");
                     controller.openStrand("recipeBook");

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

@@ -58,7 +58,7 @@ let transactionDetails = {
                     controller.createBanner(response, "error");
                 }else{
                     merchant.removeTransaction(this.transaction);
-                    controller.updateAnalytics();
+                    state.updateTransactions();
 
                     controller.openStrand("transactions", merchant.getTransactions());
                     controller.createBanner("TRANSACTION REMOVED", "success");

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

@@ -1,10 +1,30 @@
+const Merchant = require("../classes/Merchant");
+
 let account = {
     display: function(){
         document.getElementById("accountStrandTitle").innerText = merchant.name;
         document.getElementById("accountEmail").value = merchant.email;
 
         document.getElementById("accountUpdate").onclick = ()=>{this.updateData()};
+        document.getElementById("deleteMerchant").onclick = ()=>{controller.openModal("confirmDeleteMerchant")};
+
+        //Display alternate locations
+        document.getElementById("settingsAddMerchant").onclick = ()=>{controller.openModal("newMerchant")};
+        let container = document.getElementById("settingsMerchants");
+        let template = document.getElementById("locationDiv").content.children[0];
+
+        while(container.children.length > 0){
+            container.removeChild(container.firstChild);
+        }
+
+        for(let i = 0; i < merchant.owner.merchants.length; i++){
+            let div = template.cloneNode(true);
+            div.children[0].innerText = merchant.owner.merchants[i].name;
+            div.children[1].onclick = ()=>{this.switchMerchant(merchant.owner.merchants[i]._id)};
+            container.appendChild(div);
+        }
 
+        //Handle the password changey stuffs
         let passButton = document.getElementById("accountShowPassword");
         let passBox = document.getElementById("changePasswordBox");
         passButton.onclick = ()=>{
@@ -95,6 +115,71 @@ let account = {
             .finally(()=>{
                 loader.style.display = "none";
             });
+    },
+
+    switchMerchant: function(id){
+        let loader = document.getElementById("loaderContainer");
+        loader.style.display = "none";
+
+        fetch(`/merchant/${id}`)
+            .then(response => response.json())
+            .then((response)=>{
+                if(typeof(response) === "string"){
+                    controller.createBanner(response, "error");
+                }else{
+                    window.merchant = new Merchant(
+                        response[1].name,
+                        response[0].email,
+                        response[1].pos,
+                        response[1].inventory,
+                        response[1].recipes,
+                        response[2],
+                        response[0]
+                    );
+
+                    state.updateMerchant();
+                    controller.openStrand("home");
+                }
+            })
+            .catch((err)=>{
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
+            })
+            .finally(()=>{
+                loader.style.display = "none";
+            });
+    },
+
+    deleteMerchant: function(){
+        let loader = document.getElementById("loaderContainer");
+        loader.style.display = "flex";
+
+        fetch("/merchant", {method: "delete"})
+            .then(response => response.json())
+            .then((response)=>{
+                if(typeof(response) === "string"){
+                    controller.createBanner(response, "error");
+                }else{
+                    window.merchant = new Merchant(
+                        response[1].name,
+                        response[0].email,
+                        response[1].pos,
+                        response[1].inventory,
+                        response[1].recipes,
+                        response[2],
+                        response[0]
+                    );
+                    state.updateMerchant();
+
+                    controller.closeModal();
+                    controller.openStrand("home");
+                }
+            })
+            .catch((err)=>{
+                controller.createBanner("SOMETHING WENT WRONG. PLEASE REFRESH THE PAGE", "error");
+            })
+            .finally(()=>{
+                loader.style.display = "none";
+            });
     }
 }
 

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

@@ -266,6 +266,7 @@ let home = {
                             merchant.removeIngredient(merchant.getIngredient(response[i].ingredient._id));
                             merchant.addIngredients(response);
                         }
+                        state.updateIngredients();
                         controller.createBanner("INGREDIENTS UPDATED", "success");
                     }
                 })

+ 1 - 0
views/dashboardPage/js/strands/orders.js

@@ -51,6 +51,7 @@ let orders = {
                 }else{
                     merchant.clearOrders();
                     merchant.addOrders(response);
+                    state.updateOrders();
                 }
             })
             .catch((err)=>{

+ 28 - 0
views/dashboardPage/modal.ejs

@@ -26,6 +26,34 @@
 
                 <input class="button" type="submit" value="SUBMIT">
             </form>
+
+            <form id="modalNewMerchant" style="display:none;" method="post">
+                <h2>NEW MERCHANT</h2>
+                    
+                <label>NAME:
+                    <input type="text" id="addMerchantName" required>
+                </label>
+
+                <h3>CREATE WITH:</h3>
+
+                <input type="submit" class="button" value="NO POS">
+            </form>
+
+            <div id="modalConfirm" style="display:none;">
+                <svg width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                    <circle cx="12" cy="12" r="10"></circle>
+                    <line x1="12" y1="8" x2="12" y2="12"></line>
+                    <line x1="12" y1="16" x2="12.01" y2="16"></line>
+                </svg>
+
+                <p></p>
+
+                <div class="buttonBox">
+                    <button id="modalConfirmNoButton" class="button">CANCEL</button>
+
+                    <button id="modalConfirmButton" class="button">YES</button>
+                </div>
+            </div>
         </div>
     </div>
 </div>

+ 0 - 2
views/otherPages/register.ejs

@@ -65,8 +65,6 @@
 
             <button id="noneButton" class="button" formaction="/merchant/create/none">NO POS</button>
 
-            <!-- <a class="button buttonWithBorder" href="/cloverlogin">Sign Up with Clover</a> -->
-
             <a class="link" href="/login">Already have an account? Please Sign In</a>
         </form>