Kaynağa Gözat

Merge branch 'development'

Lee Morgan 5 yıl önce
ebeveyn
işleme
a23d6095a6
44 değiştirilmiş dosya ile 1418 ekleme ve 781 silme
  1. 13 0
      controllers/informationPages.js
  2. 37 1
      controllers/ingredientData.js
  3. 0 29
      controllers/merchantData.js
  4. 29 4
      controllers/orderData.js
  5. 1 1
      controllers/renderer.js
  6. 37 4
      controllers/transactionData.js
  7. 8 0
      controllers/validator.js
  8. 10 1
      models/ingredient.js
  9. 9 1
      models/order.js
  10. 7 1
      routes.js
  11. 0 0
      views/dashboardPage/bundle.js
  12. 4 2
      views/dashboardPage/dashboard.css
  13. 2 2
      views/dashboardPage/dashboard.ejs
  14. 6 1
      views/dashboardPage/js/Ingredient.js
  15. 21 12
      views/dashboardPage/js/Merchant.js
  16. 4 35
      views/dashboardPage/js/Order.js
  17. 13 2
      views/dashboardPage/js/analytics.js
  18. 83 4
      views/dashboardPage/js/dashboard.js
  19. 54 18
      views/dashboardPage/js/home.js
  20. 75 10
      views/dashboardPage/js/ingredientDetails.js
  21. 8 2
      views/dashboardPage/js/ingredients.js
  22. 32 2
      views/dashboardPage/js/newIngredient.js
  23. 30 11
      views/dashboardPage/js/newOrder.js
  24. 1 1
      views/dashboardPage/js/newRecipe.js
  25. 26 6
      views/dashboardPage/js/orderDetails.js
  26. 7 2
      views/dashboardPage/js/orders.js
  27. 11 1
      views/dashboardPage/js/transactionDetails.js
  28. 3 4
      views/dashboardPage/sidebars/ingredientDetails.ejs
  29. 17 1
      views/dashboardPage/sidebars/newIngredient.ejs
  30. 18 12
      views/dashboardPage/sidebars/newOrder.ejs
  31. 18 2
      views/dashboardPage/sidebars/orderDetails.ejs
  32. 34 2
      views/dashboardPage/sidebars/sidebars.css
  33. 0 10
      views/informationPage/help.js
  34. 0 48
      views/informationPage/information.css
  35. 0 496
      views/informationPage/information.ejs
  36. 0 13
      views/informationPage/legal.js
  37. 181 0
      views/informationPages/help.ejs
  38. 59 0
      views/informationPages/information.css
  39. 208 0
      views/informationPages/privacyPolicy.ejs
  40. 302 0
      views/informationPages/terms.ejs
  41. 10 2
      views/landingPage/landing.ejs
  42. 3 4
      views/shared/footer.ejs
  43. 11 5
      views/shared/header.ejs
  44. 26 29
      views/shared/shared.css

+ 13 - 0
controllers/informationPages.js

@@ -0,0 +1,13 @@
+module.exports = {
+    privacy: function(req, res){
+        return res.render("informationPages/privacyPolicy");
+    },
+
+    terms: function(req, res){
+        return res.render("informationPages/terms");
+    },
+
+    help: function(req, res){
+        return res.render("informationPages/help");
+    }
+}

+ 37 - 1
controllers/ingredientData.js

@@ -45,6 +45,10 @@ module.exports = {
         if(validation !== true){
             return res.json(validation);
         }
+        validation = Validator.quantity(req.body.ingredient.unitSize);
+        if(validation !== true){
+            return res.json(validation);
+        }
 
         let ingredientPromise = Ingredient.create((req.body.ingredient));
         let merchantPromise = Merchant.findOne({_id: req.session.user});
@@ -90,6 +94,9 @@ module.exports = {
             .then((ingredient)=>{
                 ingredient.name = req.body.name,
                 ingredient.category = req.body.category
+                if(ingredient.specialUnit = "bottle"){
+                    ingredient.unitSize = req.body.unitSize;
+                }
 
                 return ingredient.save();
             })
@@ -119,5 +126,34 @@ module.exports = {
             .catch((err)=>{
                 return res.json("ERROR: UNABLE TO UPDATE INGREDIENT");
             });
-    }
+    },
+
+    //POST - Removes an ingredient from the merchant's inventory
+    removeIngredient: function(req, res){
+        if(!req.session.user){
+            req.session.error = "MUST BE LOGGED IN TO DO THAT";
+            return res.redirect("/");
+        }
+
+        Merchant.findOne({_id: req.session.user})
+            .then((merchant)=>{
+                for(let i = 0; i < merchant.inventory.length; i++){
+                    if(req.params.id === merchant.inventory[i].ingredient._id.toString()){
+                        merchant.inventory.splice(i, 1);
+                        break;
+                    }
+                }
+
+                return merchant.save()
+            })
+            .then((merchant)=>{
+                return Ingredient.deleteOne({_id: req.params.id});
+            })
+            .then((ingredient)=>{
+                return res.json({});
+            })
+            .catch((err)=>{
+                return res.json("ERROR: UNABLE TO RETRIEVE USER DATA");
+            });
+    },
 }

+ 0 - 29
controllers/merchantData.js

@@ -237,35 +237,6 @@ module.exports = {
             });
     },
 
-    //POST - Removes an ingredient from the merchant's inventory
-    removeMerchantIngredient: function(req, res){
-        if(!req.session.user){
-            req.session.error = "MUST BE LOGGED IN TO DO THAT";
-            return res.redirect("/");
-        }
-
-        Merchant.findOne({_id: req.session.user})
-            .then((merchant)=>{
-                for(let i = 0; i < merchant.inventory.length; i++){
-                    if(req.params.id === merchant.inventory[i].ingredient._id.toString()){
-                        merchant.inventory.splice(i, 1);
-                        break;
-                    }
-                }
-
-                merchant.save()
-                    .then((merchant)=>{
-                        return res.json({});
-                    })
-                    .catch((err)=>{
-                        return res.json("ERROR: UNABLE TO SAVE USER DATA");
-                    });
-            })
-            .catch((err)=>{
-                return res.json("ERROR: UNABLE TO RETRIEVE USER DATA");
-            });
-    },
-
     //PUT - Update the default unit for a single ingredient
     ingredientDefaultUnit: function(req, res){
         if(!req.session.user){

+ 29 - 4
controllers/orderData.js

@@ -1,5 +1,6 @@
 const Order = require("../models/order.js");
 const Merchant = require("../models/merchant.js");
+
 const ObjectId = require("mongoose").Types.ObjectId;
 const Validator = require("./validator.js");
 
@@ -31,6 +32,8 @@ module.exports = {
             {$project: {
                 name: 1,
                 date: 1,
+                taxes: 1,
+                fees: 1,
                 ingredients: 1
             }}
         ])
@@ -96,7 +99,7 @@ module.exports = {
         ingredients: [{
             ingredient: id of the ingredient
             quantity: amount of the ingredient purchased
-            price: price for ingredient
+            pricePerUnit: price per gram
         }]
     } 
     */ 
@@ -131,7 +134,7 @@ module.exports = {
                     }
                 }
 
-                return merchant.save()
+                return merchant.save();
             })
             .then((merchant)=>{
                 return;
@@ -148,9 +151,31 @@ module.exports = {
             return res.redirect("/");
         }
 
-        Order.deleteOne({_id: req.params.id})
+        let merchant = {};
+        let order = {}
+        Merchant.findOne({_id: req.session.user})
+            .then((response)=>{
+                merchant = response;
+                return Order.findOne({_id: req.params.id});
+            })
+            .then((response)=>{
+                order = response;
+
+                return Order.deleteOne({_id: req.params.id})
+            })
             .then((response)=>{
-                return res.json({});
+                res.json({});
+
+                for(let i = 0; i < order.ingredients.length; i++){
+                    for(let j = 0; j < merchant.inventory.length; j++){
+                        if(order.ingredients[i].ingredient.toString() === merchant.inventory[j].ingredient.toString()){
+                            merchant.inventory[j].quantity -= order.ingredients[i].quantity;
+                            break;
+                        }
+                    }
+                }
+
+                return merchant.save();
             })
             .catch((err)=>{
                 return res.json("ERROR: UNABLE TO REMOVE ORDER");

+ 1 - 1
controllers/renderer.js

@@ -93,7 +93,7 @@ module.exports = {
                         merchant: new ObjectId(req.session.user),
                         date: {$gte: firstDay},
                     }},
-                    {$sort: {date: 1}},
+                    {$sort: {date: -1}},
                     {$project: {
                         date: 1,
                         recipes: 1

+ 37 - 4
controllers/transactionData.js

@@ -101,13 +101,46 @@ module.exports = {
             return res.redirect("/");
         }
 
-        Transaction.deleteOne({_id: req.params.id})
+        let merchant = {};
+        let transaction = {};
+        Merchant.findOne({_id: req.session.user})
+            .then((response)=>{
+                merchant = response;
+                return Transaction.findOne({_id: req.params.id}).populate("recipes.recipe");
+            })
             .then((response)=>{
-                return res.json({});
+                transaction = response;
+                return Transaction.deleteOne({_id: req.params.id});
+            })
+            .then((response)=>{
+                res.json({});
+
+                for(let i = 0; i < transaction.recipes.length; i++){
+                    const recipe = transaction.recipes[i].recipe;
+                    for(let j = 0; j < recipe.ingredients.length; j++){
+                        const ingredient = recipe.ingredients[i].ingredient;
+                        for(let k = 0; k < merchant.inventory.length; k++){
+                            if(ingredient.toString() == merchant.inventory[i].ingredient.toString()){
+                                merchant.inventory[i].quantity += recipe.ingredients[i].quantity * transaction.recipes[i].quantity;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                return merchant.save();
             })
             .catch((err)=>{
-                return res.json("ERROR: UNABLE TO DELETE TRANSACTION");
-            });
+                return res.json("ERROR: UNABLE TO DELETE THE TRANSACTION");
+            })
+
+        // Transaction.deleteOne({_id: req.params.id})
+        //     .then((response)=>{
+        //         return res.json({});
+        //     })
+        //     .catch((err)=>{
+        //         return res.json("ERROR: UNABLE TO DELETE TRANSACTION");
+        //     });
     },
 
     getTransactionsByDate: function(req, res){

+ 8 - 0
controllers/validator.js

@@ -105,6 +105,14 @@ module.exports = {
             return "Date cannot be in the future";
         }
 
+        if(this.quantity(order.taxes) !== true){
+            return "TAXES MUST BE A NON NEGATIVE NUMBER";
+        }
+
+        if(this.quantity(order.fees) !== true){
+            return "FEES MUST BE A NON NEGATIVE NUMBER";
+        }
+
         for(let i = 0; i < order.ingredients; i++){
             let quantityCheck = this.quantity(order.ingredients[i].quantity);
             if(quantityCheck !== true){

+ 10 - 1
models/ingredient.js

@@ -8,11 +8,20 @@ const IngredientSchema = new mongoose.Schema({
     },
     category: {
         type: String,
-        minlength: 3
+        minlength: 3,
+        required: true
     },
     unitType: {
         type: String,
         required: true
+    },
+    specialUnit: {
+        type: String,
+        required: false
+    },
+    unitSize:{
+        type: Number,
+        required: false
     }
 });
 

+ 9 - 1
models/order.js

@@ -12,6 +12,14 @@ const OrderSchema = new mongoose.Schema({
         default: Date.now,
         required: true
     },
+    taxes: {
+        type: Number,
+        required: true
+    },
+    fees: {
+        type: Number,
+        required: true
+    },
     ingredients: [{
         ingredient: {
             type: mongoose.Schema.Types.ObjectId,
@@ -23,7 +31,7 @@ const OrderSchema = new mongoose.Schema({
             required: true,
             min: 0
         },
-        price: {
+        pricePerUnit: {
             type: Number,
             min: 0
         }

+ 7 - 1
routes.js

@@ -5,6 +5,7 @@ const otherData = require("./controllers/otherData");
 const transactionData = require("./controllers/transactionData");
 const recipeData = require("./controllers/recipeData");
 const orderData = require("./controllers/orderData.js");
+const informationPages = require("./controllers/informationPages.js");
 
 module.exports = function(app){
     //Render page
@@ -18,7 +19,6 @@ module.exports = function(app){
     app.get("/merchant/create/clover", merchantData.createMerchantClover);
     app.get("/merchant/create/square", merchantData.createMerchantSquare);
     app.delete("/merchant/recipes/remove/:id", merchantData.removeRecipe);
-    app.delete("/merchant/ingredients/remove/:id", merchantData.removeMerchantIngredient);
     app.put("/merchant/ingredients/update/:id/:unit", merchantData.ingredientDefaultUnit);
     app.put("/merchant/ingredients/update", merchantData.updateMerchantIngredient); //also updates some data in ingredients
     app.post("/merchant/password", merchantData.updatePassword);
@@ -27,6 +27,7 @@ module.exports = function(app){
     app.get("/ingredients", ingredientData.getIngredients);
     app.post("/ingredients/create", ingredientData.createIngredient);  //also adds to merchant
     app.put("/ingredients/update", ingredientData.updateIngredient);
+    app.delete("/ingredients/remove/:id", ingredientData.removeIngredient);
 
     //Recipes
     app.post("/recipe/create", recipeData.createRecipe);
@@ -54,4 +55,9 @@ module.exports = function(app){
     app.get("/squarelogin", otherData.squareRedirect);
     app.get("/cloverauth*", otherData.cloverAuth);
     app.get("/squareauth", otherData.squareAuth);
+
+    //Information Pages
+    app.get("/privacy", informationPages.privacy);
+    app.get("/terms", informationPages.terms);
+    app.get("/help", informationPages.help);
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
views/dashboardPage/bundle.js


+ 4 - 2
views/dashboardPage/dashboard.css

@@ -270,13 +270,13 @@ Home Strand
         }
 
         .ingredientCheck p:last-of-type{
-            width: 15%
+            width: 20%
         }
 
         .numberInput{
             display: flex;
             justify-content: center;
-            width: 45%;
+            width: 40%;
             margin-right: 25px;
         }
 
@@ -322,6 +322,8 @@ Home Strand
         align-items: center;
         flex-basis: 100px;
         flex-grow: 2.5;
+        padding: 5px;
+        box-sizing: border-box;
 
     }
         #popularCanvas{

+ 2 - 2
views/dashboardPage/dashboard.ejs

@@ -2,7 +2,7 @@
 <html>
     <head>
         <meta charset="UTF-8">
-        <meta content="width=device-width, initial-scale=1" name="viewport" />
+        <meta content="width=device-width, initial-scale=1" name="viewport"/>
         <title>The Subline</title>
         <link rel="icon" type="img/png" href="/shared/images/logo.png">
         <link rel="stylesheet" href="/shared/shared.css">
@@ -35,7 +35,7 @@
                     <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                     <polyline points="9 22 9 12 15 12 15 22"></polyline>
                 </svg>
-                <p>HOME</p>
+                <p>DASHBOARD</p>
             </button>
         
             <button class="menuButton" id="ingredientsBtn" onclick="controller.openStrand('ingredients')">

+ 6 - 1
views/dashboardPage/js/Ingredient.js

@@ -1,11 +1,16 @@
 class Ingredient{
-    constructor(id, name, category, unitType, unit, parent){
+    constructor(id, name, category, unitType, unit, parent, specialUnit = undefined, unitSize = undefined){
         this.id = id;
         this.name = name;
         this.category = category;
         this.unitType = unitType;
         this.unit = unit;
         this.parent = parent;
+        if(specialUnit){
+            this.specialUnit = specialUnit;
+            this.unitSize = unitSize;
+        }
+        
     }
 
     convert(quantity){

+ 21 - 12
views/dashboardPage/js/Merchant.js

@@ -13,11 +13,12 @@ class Merchant{
         this.units = {
             mass: ["g", "kg", "oz", "lb"],
             volume: ["ml", "l", "tsp", "tbsp", "ozfl", "cup", "pt", "qt", "gal"],
-            length: ["mm", "cm", "m", "in", "foot"],
-            other: ["each"]
+            length: ["mm", "cm", "m", "in", "ft"],
+            other: ["each", "bottle"]
         }
         
         for(let i = 0; i < oldMerchant.inventory.length; i++){
+
             this.ingredients.push({
                 ingredient: new Ingredient(
                     oldMerchant.inventory[i].ingredient._id,
@@ -25,7 +26,9 @@ class Merchant{
                     oldMerchant.inventory[i].ingredient.category,
                     oldMerchant.inventory[i].ingredient.unitType,
                     oldMerchant.inventory[i].defaultUnit,
-                    this
+                    this,
+                    oldMerchant.inventory[i].ingredient.specialUnit,
+                    oldMerchant.inventory[i].ingredient.unitSize
                 ),
                 quantity: oldMerchant.inventory[i].quantity
             });
@@ -54,11 +57,10 @@ class Merchant{
     /*
     Updates all specified item in the merchant's inventory and updates the page
     If ingredient doesn't exist, add it
-    ingredients = {
+    ingredients = [{
         ingredient: Ingredient object,
         quantity: new quantity,
-        defaultUnit: the default unit to be displayed
-    }
+    }]
     remove = set true if removing
     isOrder = set true if this is coming from an order
     */
@@ -67,7 +69,7 @@ class Merchant{
             let isNew = true;
             for(let j = 0; j < this.ingredients.length; j++){
                 if(this.ingredients[j].ingredient === ingredients[i].ingredient){
-                    if(remove){
+                    if(remove && !isOrder){
                         this.ingredients.splice(j, 1);
                     }else if(!remove && isOrder){
                         this.ingredients[j].quantity += ingredients[i].quantity;
@@ -83,8 +85,7 @@ class Merchant{
             if(isNew){
                 this.ingredients.push({
                     ingredient: ingredients[i].ingredient,
-                    quantity: parseFloat(ingredients[i].quantity),
-                    defaultUnit: ingredients[i].defaultUnit
+                    quantity: parseFloat(ingredients[i].quantity)
                 });
             }
         }
@@ -178,7 +179,7 @@ class Merchant{
 
         if(isNew){
             this.transactions.push(transaction);
-            this.transactions.sort((a, b) => a.date > b.date ? 1 : -1);
+            this.transactions.sort((a, b) => a.date > b.date ? -1 : 1);
         }
 
         let keys = Object.keys(ingredients);
@@ -349,8 +350,9 @@ class Merchant{
 
         for(let i = 0; i < this.ingredients.length; i++){
             let unitExists = false;
+            const innerIngredient = this.ingredients[i].ingredient;
             for(let j = 0; j < ingredientsByUnit.length; j++){
-                if(this.ingredients[i].ingredient.unit === ingredientsByUnit[j].name){
+                if(innerIngredient.unit === ingredientsByUnit[j].name || innerIngredient.specialUnit === ingredientsByUnit[j].name){
                     ingredientsByUnit[j].ingredients.push(this.ingredients[i]);
 
                     unitExists = true;
@@ -359,8 +361,15 @@ class Merchant{
             }
 
             if(!unitExists){
+                let unit = "";
+                if(innerIngredient.specialUnit === "bottle"){
+                    unit = "bottle";
+                }else{
+                    unit = innerIngredient.unit;
+                }
+
                 ingredientsByUnit.push({
-                    name: this.ingredients[i].ingredient.unit,
+                    name: unit,
                     ingredients: [this.ingredients[i]]
                 });
             }

+ 4 - 35
views/dashboardPage/js/Order.js

@@ -1,8 +1,10 @@
 class Order{
-    constructor(id, name, date, ingredients, parent){
+    constructor(id, name, date, taxes, fees, ingredients, parent){
         this.id = id;
         this.name = name;
         this.date = new Date(date);
+        this.taxes = taxes;
+        this.fees = fees;
         this.ingredients = [];
         this.parent = parent;
 
@@ -12,7 +14,7 @@ class Order{
                     this.ingredients.push({
                         ingredient: parent.ingredients[j].ingredient,
                         quantity: ingredients[i].quantity,
-                        price: ingredients[i].price
+                        pricePerUnit: ingredients[i].pricePerUnit
                     });
 
                     break;
@@ -20,39 +22,6 @@ class Order{
             }
         }
     }
-
-    convertPrice(unitType, unit, price){
-        if(unitType === "mass"){
-            switch(unit){
-                case "g": break;
-                case "kg": price *= 1000; break;
-                case "oz":  price *= 28.3495; break;
-                case "lb":  price *= 453.5924; break;
-            }
-        }else if(unitType === "volume"){
-            switch(unit){
-                case "ml": price /= 1000; break;
-                case "l": break;
-                case "tsp": price /= 202.8842; break;
-                case "tbsp": price /= 67.6278; break;
-                case "ozfl": price /= 33.8141; break;
-                case "cup": price /= 4.1667; break;
-                case "pt": price /= 2.1134; break;
-                case "qt": price /= 1.0567; break;
-                case "gal": price *= 3.7854; break;
-            }
-        }else if(unitType === "length"){
-            switch(unit){
-                case "mm": price /= 1000; break;
-                case "cm": price /= 100; break;
-                case "m": break;
-                case "in": price /= 39.3701; break;
-                case "ft": price /= 3.2808; break;
-            }
-        }
-
-        return price;
-    }
 }
 
 module.exports = Order;

+ 13 - 2
views/dashboardPage/js/analytics.js

@@ -1,4 +1,5 @@
 let analytics = {
+    newData: false,
     dateChange: false,
     transactions: [],
     ingredient: {},
@@ -7,12 +8,12 @@ let analytics = {
     display: function(Transaction){
         document.getElementById("analDateBtn").onclick = ()=>{this.changeDates(Transaction)};
 
-        if(this.transactions.length === 0){
+        if(this.transactions.length === 0 || this.newData === true){
             let startDate = new Date();
             startDate.setMonth(startDate.getMonth() - 1);
             const dateIndices = controller.transactionIndices(merchant.transactions, startDate);
 
-            this.transactions = merchant.transactions.slice(dateIndices[0], dateIndices[1]);
+            this.transactions = merchant.transactions.slice(dateIndices[0], dateIndices[1] + 1);
         }
 
         let slider = document.getElementById("analSlider");
@@ -141,6 +142,11 @@ let analytics = {
                     }
                 }
             }
+
+            if(i === this.transactions.length - 1){
+                quantities.push(this.ingredient.ingredient.convert(currentQuantity));
+                dates.push(currentDate);
+            }
         }
 
         let trace = {
@@ -217,6 +223,11 @@ let analytics = {
                     quantity += recipe.quantity;
                 }
             }
+
+            if(i === this.transactions.length - 1){
+                quantities.push(quantity);
+                dates.push(currentDate);
+            }
         }
 
         const trace = {

+ 83 - 4
views/dashboardPage/js/dashboard.js

@@ -240,6 +240,7 @@ controller = {
             case "transaction":
                 transactions.isPopulated = false;
                 transactions.display(Transaction);
+                analytics.newData = true;
                 break;
         }
     },
@@ -258,14 +259,14 @@ controller = {
         let indices = [];
 
         for(let i = 0; i < transactions.length; i++){
-            if(transactions[i].date > from){
+            if(transactions[i].date < to){
                 indices.push(i);
                 break;
             }
         }
 
-        for(let i = transactions.length - 1; i >=0; i--){
-            if(transactions[i].date < to){
+        for(let i = transactions.length - 1; i >= 0; i--){
+            if(transactions[i].date > from){
                 indices.push(i);
                 break;
             }
@@ -276,7 +277,85 @@ controller = {
         }
 
         return indices;
-    }
+    },
+
+    /*
+    Converts the price of a unit to $/main unit
+    unitType = type of the unit (i.e. mass, volume)
+    unit = exact unit to convert from
+    price = price of the ingredient per unit in cents
+    */
+    convertPrice(unitType, unit, price){
+        if(unitType === "mass"){
+            switch(unit){
+                case "g": break;
+                case "kg": price /= 1000; break;
+                case "oz":  price /= 28.3495; break;
+                case "lb":  price /= 453.5924; break;
+            }
+        }else if(unitType === "volume"){
+            switch(unit){
+                case "ml": price *= 1000; break;
+                case "l": break;
+                case "tsp": price *= 202.8842; break;
+                case "tbsp": price *= 67.6278; break;
+                case "ozfl": price *= 33.8141; break;
+                case "cup": price *= 4.1667; break;
+                case "pt": price *= 2.1134; break;
+                case "qt": price *= 1.0567; break;
+                case "gal": price /= 3.7854; break;
+            }
+        }else if(unitType === "length"){
+            switch(unit){
+                case "mm": price *= 1000; break;
+                case "cm": price *= 100; break;
+                case "m": break;
+                case "in": price *= 39.3701; break;
+                case "ft": price *= 3.2808; break;
+            }
+        }
+
+        return price;
+    },
+
+    /*
+    Converts the price of unit back to the price per default unit
+    unitType = type of the unit (i.e. mass, volume)
+    unit = exact unit to convert to
+    price = price of the ingredient per unit in cents
+    */
+    reconvertPrice(unitType, unit, price){
+        if(unitType === "mass"){
+            switch(unit){
+                case "g": break;
+                case "kg": price *= 1000; break;
+                case "oz":  price *= 28.3495; break;
+                case "lb":  price *= 453.5924; break;
+            }
+        }else if(unitType === "volume"){
+            switch(unit){
+                case "ml": price /= 1000; break;
+                case "l": break;
+                case "tsp": price /= 202.8842; break;
+                case "tbsp": price /= 67.6278; break;
+                case "ozfl": price /= 33.8141; break;
+                case "cup": price /= 4.1667; break;
+                case "pt": price /= 2.1134; break;
+                case "qt": price /= 1.0567; break;
+                case "gal": price *= 3.7854; break;
+            }
+        }else if(unitType === "length"){
+            switch(unit){
+                case "mm": price /= 1000; break;
+                case "cm": price /= 100; break;
+                case "m": break;
+                case "in": price /= 39.3701; break;
+                case "ft": price /= 3.2808; break;
+            }
+        }
+
+    return price;
+}
 }
 
 if(window.screen.availWidth > 1000 && window.screen.availWidth <= 1400){

+ 54 - 18
views/dashboardPage/js/home.js

@@ -19,11 +19,11 @@ let home = {
         let lastMonthToDay = new Date(new Date().setMonth(today.getMonth() - 1));
 
         let revenueThisMonth = merchant.revenue(controller.transactionIndices(merchant.transactions, firstOfMonth));
-        let revenueLastmonthToDay = merchant.revenue(controller.transactionIndices(merchant.transactions, firstOfLastMonth, lastMonthToDay));
+        let revenueLastMonthToDay = merchant.revenue(controller.transactionIndices(merchant.transactions, firstOfLastMonth, lastMonthToDay));
 
         document.getElementById("revenue").innerText = `$${revenueThisMonth.toLocaleString("en")}`;
 
-        let revenueChange = ((revenueThisMonth - revenueLastmonthToDay) / revenueLastmonthToDay) * 100;
+        let revenueChange = ((revenueThisMonth - revenueLastMonthToDay) / revenueLastMonthToDay) * 100;
         
         let img = "";
         if(revenueChange >= 0){
@@ -112,10 +112,25 @@ let home = {
 
             ingredientCheck.ingredient = ingredient;
             ingredientCheck.children[0].innerText = ingredient.ingredient.name;
-            ingredientCheck.children[1].children[0].onclick = ()=>{input.value--};
-            input.value = ingredient.ingredient.convert(ingredient.quantity).toFixed(2);
-            ingredientCheck.children[1].children[2].onclick = ()=>{input.value++}
-            ingredientCheck.children[2].innerText = ingredient.ingredient.unit.toUpperCase();
+            ingredientCheck.children[1].children[0].onclick = ()=>{
+                input.value--;
+                input.changed = true;
+            };
+            if(ingredient.ingredient.specialUnit === "bottle"){
+                input.value = (ingredient.quantity / ingredient.ingredient.unitSize).toFixed(2);
+                ingredientCheck.children[2].innerText = "BOTTLES";
+            }else{
+                input.value = ingredient.ingredient.convert(ingredient.quantity).toFixed(2);
+                ingredientCheck.children[2].innerText = ingredient.ingredient.unit.toUpperCase();
+            }
+
+            
+            ingredientCheck.children[1].children[2].onclick = ()=>{
+                input.value++;
+                input.changed = true;
+            }
+            input.onchange = ()=>{input.changed = true};
+            
 
             ul.appendChild(ingredientCheck);
         }
@@ -129,16 +144,28 @@ let home = {
 
         let ingredientList = merchant.ingredientsSold(controller.transactionIndices(merchant.transactions, thisMonth));
         if(ingredientList !== false){
-            ingredientList.sort((a, b) => a.quantity < b.quantity);
+            ingredientList.sort((a, b)=>{
+                if(a.quantity < b.quantity){
+                    return 1;
+                }
+                if(a.quantity > b.quantity){
+                    return -1;
+                }
+
+                return 0;
+            });
 
             let quantities = [];
-            let names = [];
             let labels = [];
             let colors = [];
-            for(let i = 4; i >= 0; i--){
+            let count = (ingredientList.length < 5) ? ingredientList.length - 1 : 4;
+            for(let i = count; i >= 0; i--){
+                const ingredientName = ingredientList[i].ingredient.name;
+                const ingredientQuantity = ingredientList[i].ingredient.convert(ingredientList[i].quantity);
+                const unitName = ingredientList[i].ingredient.unit;
+
                 quantities.push(ingredientList[i].quantity);
-                names.push(ingredientList[i].ingredient.name.toUpperCase());
-                labels.push(`${ingredientList[i].ingredient.convert(ingredientList[i].quantity).toFixed(2)} ${ingredientList[i].ingredient.unit.toUpperCase()}`);
+                labels.push(`${ingredientName}: ${ingredientQuantity.toFixed(2)} ${unitName.toUpperCase()}`);
                 if(i === 0){
                     colors.push("rgb(255, 99, 107");
                 }else{
@@ -148,7 +175,6 @@ let home = {
 
             let trace = {
                 x: quantities,
-                y: names,
                 type: "bar",
                 orientation: "h",
                 text: labels,
@@ -164,6 +190,9 @@ let home = {
                 xaxis: {
                     zeroline: false,
                     title: "QUANTITY"
+                },
+                yaxis: {
+                    showticklabels: false
                 }
             }
             
@@ -188,11 +217,16 @@ let home = {
             if(lis[i].children[1].children[1].value >= 0){
                 let merchIngredient = lis[i].ingredient;
 
-                let value = parseFloat(lis[i].children[1].children[1].value);
+                if(lis[i].children[1].children[1].changed === true){
+                    let value = 0;
+                    if(merchIngredient.ingredient.specialUnit === "bottle"){
+                        value = parseFloat(lis[i].children[1].children[1].value) * merchIngredient.ingredient.unitSize;
+                    }else{
+                        value = controller.convertToMain(merchIngredient.ingredient.unit, parseFloat(lis[i].children[1].children[1].value));
+                    }
+                    
 
-                if(value !== merchIngredient.quantity){
                     changes.push({
-                        id: merchIngredient.ingredient.id,
                         ingredient: merchIngredient.ingredient,
                         quantity: value
                     });
@@ -201,17 +235,19 @@ let home = {
                         id: merchIngredient.ingredient.id,
                         quantity: value
                     });
+
+                    lis[i].children[1].children[1].changed = false;
                 }
             }else{
                 banner.createError("CANNOT HAVE NEGATIVE INGREDIENTS");
                 return;
             }
         }
-
-        let loader = document.getElementById("loaderContainer");
-        loader.style.display = "flex";
         
         if(fetchData.length > 0){
+            let loader = document.getElementById("loaderContainer");
+            loader.style.display = "flex";
+
             fetch("/merchant/ingredients/update", {
                 method: "PUT",
                 headers: {

+ 75 - 10
views/dashboardPage/js/ingredientDetails.js

@@ -5,6 +5,16 @@ let ingredientDetails = {
     display: function(ingredient){
         this.ingredient = ingredient;
 
+        if(this.ingredient.ingredient.specialUnit === "bottle"){
+            try{
+                let label = document.getElementById("ingredientUnitSizeLabel");
+                label.parentElement.removeChild(label);
+
+                let border = document.getElementById("bottleBorder");
+                border.parentElement.removeChild(border);
+            }catch(err){}
+        }
+
         document.getElementById("ingredientDetailsCategory").innerText = ingredient.ingredient.category;
 
         let categoryInput = document.getElementById("detailsCategoryInput");
@@ -17,10 +27,21 @@ let ingredientDetails = {
         nameInput.value = "";
         nameInput.placeholder = ingredient.ingredient.name;
 
+        //Display the stock quantity (and the edit input) based on the unit type
         let stockInput = document.getElementById("ingredientInput");
-        document.getElementById("ingredientStock").innerText = `${ingredient.ingredient.convert(ingredient.quantity).toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
+        let stockDisplay = document.getElementById("ingredientStock");
         stockInput.value = "";
-        stockInput.placeholder = ingredient.ingredient.convert(ingredient.quantity).toFixed(2);
+        if(ingredient.ingredient.specialUnit === "bottle"){
+            let quantity = ingredient.ingredient.convert(ingredient.quantity);
+
+            stockDisplay.innerText = `${quantity.toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
+            stockDisplay.innerText = `${(ingredient.quantity / ingredient.ingredient.unitSize).toFixed(2)} BOTTLES`;
+            stockInput.placeholder = quantity.toFixed(2);
+        }else{
+            stockDisplay.innerText = `${ingredient.ingredient.convert(ingredient.quantity).toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
+            stockInput.placeholder = ingredient.ingredient.convert(ingredient.quantity).toFixed(2);
+        }
+
 
         let quantities = [];
         let now = new Date();
@@ -42,7 +63,7 @@ let ingredientDetails = {
         }
 
         let dailyUse = sum / quantities.length;
-        document.getElementById("dailyUse").innerText = `${ingredient.ingredient.convert(dailyUse).toFixed(2)} ${ingredient.ingredient.unit}`;
+        document.getElementById("dailyUse").innerText = `${ingredient.ingredient.convert(dailyUse).toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
 
         let ul = document.getElementById("ingredientRecipeList");
         let recipes = merchant.getRecipesForIngredient(ingredient.ingredient);
@@ -109,7 +130,7 @@ let ingredientDetails = {
         let loader = document.getElementById("loaderContainer");
         loader.style.display = "flex";
 
-        fetch(`/merchant/ingredients/remove/${this.ingredient.ingredient.id}`, {
+        fetch(`/ingredients/remove/${this.ingredient.ingredient.id}`, {
             method: "DELETE",
         })
             .then((response) => response.json())
@@ -138,23 +159,61 @@ let ingredientDetails = {
         for(let i = 0; i < remove.length; i++){
             remove[i].style.display = "none";
         }
+
+        if(this.ingredient.ingredient.specialUnit === "bottle"){
+            const mainDiv = document.getElementById("ingredientDetails");
+            const displayUnits = document.getElementById("displayUnitLabel");
+
+            let label = document.createElement("label");
+            label.innerText = `BOTTLE SIZE (${this.ingredient.ingredient.unit.toUpperCase()})`;
+            label.id = "ingredientUnitSizeLabel";
+            mainDiv.insertBefore(label, displayUnits);
+
+            const convQuant = this.ingredient.ingredient.convert(this.ingredient.ingredient.unitSize);
+
+            let input = document.createElement("input");
+            input.type = "number";
+            input.value = convQuant.toFixed(2);
+            input.id = "ingredientUnitSizeIn";
+            input.min = "0";
+            input.step = "0.01";
+            label.appendChild(input);
+
+            let border = document.createElement("div");
+            border.classList.add("lineBorder");
+            border.id="bottleBorder";
+            mainDiv.insertBefore(border, displayUnits);
+        }
     },
 
     editSubmit: function(){
+        //Update the ingredient unit depending on the type of unit
         let ingredientButtons = document.querySelectorAll(".unitButton");
         for(let i = 0; i < ingredientButtons.length; i++){
             if(ingredientButtons[i].classList.contains("unitActive")){
-                this.ingredient.ingredient.unit = ingredientButtons[i].innerText.toLowerCase();
+                const unit = ingredientButtons[i].innerText.toLowerCase();
+                this.ingredient.ingredient.unit = unit;
+
                 break;
             }
         }
 
+        //Update the ingredient quantity depending on the type of unit
         const quantityElem = document.getElementById("ingredientInput");
         if(quantityElem.value !== ""){
-            this.ingredient.quantity = controller.convertToMain(
-                this.ingredient.ingredient.unit,
-                Number(document.getElementById("ingredientInput").value)
-            );
+            if(this.ingredient.ingredient.specialUnit === "bottle"){
+                let quantInMain = controller.convertToMain(
+                    this.ingredient.ingredient.unit,
+                    quantityElem.value * this.ingredient.ingredient.unitSize
+                );
+
+                this.ingredient.quantity = quantInMain / this.ingredient.ingredient.unitSize;
+            }else{
+                this.ingredient.quantity = controller.convertToMain(
+                    this.ingredient.ingredient.unit,
+                    Number(quantityElem.value)
+                );
+            }
         }
 
         const category = document.getElementById("detailsCategoryInput");
@@ -162,7 +221,7 @@ let ingredientDetails = {
 
         const name = document.getElementById("ingredientDetailsNameIn");
         this.ingredient.ingredient.name = (name.value === "") ? this.ingredient.ingredient.name : name.value;
-        
+
         let data = {
             id: this.ingredient.ingredient.id,
             name: this.ingredient.ingredient.name,
@@ -171,6 +230,12 @@ let ingredientDetails = {
             defaultUnit: this.ingredient.ingredient.unit
         };
 
+        if(this.ingredient.ingredient.specialUnit === "bottle"){
+            const unitSize = document.getElementById("ingredientUnitSizeIn").value;
+            this.ingredient.ingredient.unitSize = controller.convertToMain(this.ingredient.ingredient.unit, unitSize);
+            data.unitSize = this.ingredient.ingredient.unitSize;
+        }
+
         let loader = document.getElementById("loaderContainer");
         loader.style.display = "flex";
 

+ 8 - 2
views/dashboardPage/js/ingredients.js

@@ -33,7 +33,7 @@ let ingredients = {
 
         for(let i = 0; i < categories.length; i++){
             let categoryDiv = categoryTemplate.cloneNode(true);
-            categoryDiv.children[0].children[0].innerText = categories[i].name;
+            categoryDiv.children[0].children[0].innerText = categories[i].name.toUpperCase();
             
             categoryDiv.children[0].children[1].onclick = ()=>{
                 this.toggleCategory(categoryDiv.children[1], categoryDiv.children[0].children[1]);
@@ -46,11 +46,17 @@ let ingredients = {
                 let ingredientDiv = ingredientTemplate.cloneNode(true);
 
                 ingredientDiv.children[0].innerText = ingredient.ingredient.name;
-                ingredientDiv.children[2].innerText = `${ingredient.ingredient.convert(ingredient.quantity).toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
                 ingredientDiv.onclick = ()=>{controller.openSidebar("ingredientDetails", ingredient)};
                 ingredientDiv._name = ingredient.ingredient.name.toLowerCase();
                 ingredientDiv._unit = ingredient.ingredient.unit.toLowerCase();
 
+                
+                if(ingredient.ingredient.specialUnit === "bottle"){
+                    ingredientDiv.children[2].innerText = `${(ingredient.quantity / ingredient.ingredient.unitSize).toFixed(2)} BOTTLES`
+                }else{
+                    ingredientDiv.children[2].innerText = `${ingredient.ingredient.convert(ingredient.quantity).toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
+                }
+
                 categoryDiv.children[1].appendChild(ingredientDiv);
                 this.ingredients.push(ingredientDiv);
             }

+ 32 - 2
views/dashboardPage/js/newIngredient.js

@@ -1,15 +1,31 @@
 let newIngredient = {
     display: function(Ingredient){
+        const selector = document.getElementById("unitSelector");
+
         document.getElementById("newIngName").value = "";
         document.getElementById("newIngCategory").value = "";
         document.getElementById("newIngQuantity").value = 0;
+        document.getElementById("bottleSizeLabel").style.display = "none";
+        selector.value = "g";
 
+        selector.onchange = ()=>{this.unitChange()};
         document.getElementById("submitNewIng").onclick = ()=>{this.submit(Ingredient)};
     },
 
+    unitChange: function(){
+        const select = document.getElementById("unitSelector");
+        const bottleLabel = document.getElementById("bottleSizeLabel");
+        if(select.value === "bottle"){
+            bottleLabel.style.display = "block";
+        }else{
+            bottleLabel.style.display = "none";
+        }
+    },
+
     submit: function(Ingredient){
         let unitSelector = document.getElementById("unitSelector");
         let options = document.querySelectorAll("#unitSelector option");
+        const quantityValue = document.getElementById("newIngQuantity").value;
 
         let unit = unitSelector.value;
 
@@ -19,10 +35,22 @@ let newIngredient = {
                 category: document.getElementById("newIngCategory").value,
                 unitType: options[unitSelector.selectedIndex].getAttribute("type"),
             },
-            quantity: controller.convertToMain(unit, document.getElementById("newIngQuantity").value),
+            quantity: controller.convertToMain(unit, quantityValue),
             defaultUnit: unit
         }
 
+        //Change the ingredient if it is a special unit type (ie "bottle")
+        if(unit === "bottle"){
+            const bottleUnit = document.getElementById("bottleUnits").value;
+            const bottleSize = controller.convertToMain(bottleUnit, document.getElementById("bottleSize").value);
+
+            newIngredient.ingredient.unitType = "volume";
+            newIngredient.ingredient.unitSize = bottleSize;
+            newIngredient.defaultUnit = bottleUnit;
+            newIngredient.ingredient.specialUnit = unit;
+            newIngredient.quantity = quantityValue * bottleSize;
+        }
+    
         let loader = document.getElementById("loaderContainer");
         loader.style.display = "flex";
 
@@ -45,7 +73,9 @@ let newIngredient = {
                             response.ingredient.category,
                             response.ingredient.unitType,
                             response.defaultUnit,
-                            merchant
+                            merchant,
+                            response.ingredient.specialUnit,
+                            response.ingredient.unitSize
                         ),
                         quantity: response.quantity
                     }]);

+ 30 - 11
views/dashboardPage/js/newOrder.js

@@ -29,8 +29,15 @@ let newOrder = {
 
         let div = document.getElementById("selectedIngredient").content.children[0].cloneNode(true);
         div.ingredient = ingredient;
-        div.children[0].children[0].innerText = `${ingredient.name} (${ingredient.unit.toUpperCase()})`;
         div.children[0].children[1].onclick = ()=>{this.removeIngredient(div, element)};
+
+        //Display units depending on the whether it is a special unit
+        if(ingredient.specialUnit === "bottle"){
+            div.children[0].children[0].innerText = `${ingredient.name} (BOTTLES)`;
+        }else{
+            div.children[0].children[0].innerText = `${ingredient.name} (${ingredient.unit.toUpperCase()})`;
+        }
+
         document.getElementById("selectedIngredientList").appendChild(div);
     },
 
@@ -41,21 +48,20 @@ let newOrder = {
 
     submit: function(Order){
         let date = document.getElementById("newOrderDate").value;
-        let time = document.getElementById("newOrderTime").value;
+        let taxes = document.getElementById("orderTaxes").value * 100;
+        let fees = document.getElementById("orderFees").value * 100;
         let ingredients = document.getElementById("selectedIngredientList").children;
 
         if(date === ""){
             banner.createError("DATE IS REQUIRED FOR ORDERS");
             return;
         }
-        
-        if(time !== ""){
-            date = `${date}T${time}`;
-        }
 
         let data = {
             name: document.getElementById("newOrderName").value,
             date: date,
+            taxes: taxes,
+            fees: fees,
             ingredients: []
         }
 
@@ -72,11 +78,21 @@ let newOrder = {
                 banner.createError("QUANTITY AND PRICE MUST BE NON-NEGATIVE NUMBERS");
             }
 
-            data.ingredients.push({
-                ingredient: ingredients[i].ingredient.id,
-                quantity: controller.convertToMain(ingredients[i].ingredient.unit, quantity),
-                price: price * 100
-            });
+            if(ingredients[i].ingredient.specialUnit === "bottle"){
+                const ppu = controller.convertPrice("volume", ingredients[i].ingredient.unit, (price * 100) / (ingredients[i].ingredient.convert(ingredients[i].ingredient.unitSize)));
+
+                data.ingredients.push({
+                    ingredient: ingredients[i].ingredient.id,
+                    quantity: quantity * ingredients[i].ingredient.unitSize,
+                    pricePerUnit: ppu,
+                });
+            }else{
+                data.ingredients.push({
+                    ingredient: ingredients[i].ingredient.id,
+                    quantity: controller.convertToMain(ingredients[i].ingredient.unit, quantity),
+                    pricePerUnit: controller.convertPrice(ingredients[i].ingredient.unitType, ingredients[i].ingredient.unit, price * 100)
+                });
+            }
         }
 
         let loader = document.getElementById("loaderContainer");
@@ -98,12 +114,15 @@ let newOrder = {
                         response._id,
                         response.name,
                         response.date,
+                        response.taxes,
+                        response.fees,
                         response.ingredients,
                         merchant
                     );
 
                     merchant.editOrders([order]);
                     merchant.editIngredients(order.ingredients, false, true);
+                    banner.createNotification("NEW ORDER CREATED");
                 }
             })
             .catch((err)=>{

+ 1 - 1
views/dashboardPage/js/newRecipe.js

@@ -20,7 +20,7 @@ let newRecipe = {
             }
         }
 
-        document.getElementById("ingredientCount").onclick = ()=>{this.changeRecipeCount()};
+        document.getElementById("ingredientCount").onchange = ()=>{this.changeRecipeCount()};
         document.getElementById("submitNewRecipe").onclick = ()=>{this.submit(Recipe)};
     },
 

+ 26 - 6
views/dashboardPage/js/orderDetails.js

@@ -4,7 +4,8 @@ let orderDetails = {
 
         document.getElementById("orderDetailName").innerText = order.name;
         document.getElementById("orderDetailDate").innerText = order.date.toLocaleDateString("en-US");
-        document.getElementById("orderDetailTime").innerText = order.date.toLocaleTimeString("en-US");
+        document.getElementById("orderDetailTax").innerText = `$${(order.taxes / 100).toFixed(2)}`;
+        document.getElementById("orderDetailFee").innerText = `$${(order.fees / 100).toFixed(2)}`;
 
         let ingredientList = document.getElementById("orderIngredients");
         while(ingredientList.children.length > 0){
@@ -15,18 +16,32 @@ let orderDetails = {
         let grandTotal = 0;
         for(let i = 0; i < order.ingredients.length; i++){
             let ingredientDiv = template.cloneNode(true);
-            let price = order.ingredients[i].price / 100;
+            let price = order.ingredients[i].pricePerUnit * order.ingredients[i].quantity;
             grandTotal += price;
 
-            let ingredient = order.ingredients[i].ingredient;
+            const ingredient = order.ingredients[i].ingredient;
+            
             ingredientDiv.children[0].innerText = order.ingredients[i].ingredient.name;
-            ingredientDiv.children[1].innerText = `${ingredient.convert(order.ingredients[i].quantity).toFixed(2)} ${ingredient.unit.toUpperCase()}`;
-            ingredientDiv.children[2].innerText = `$${price.toFixed(2)}`;
+            ingredientDiv.children[2].innerText = `$${(price / 100).toFixed(2)}`;
+            
+            const ingredientDisplay = ingredientDiv.children[1];
+            if(ingredient.specialUnit === "bottle"){
+                const quantSold = order.ingredients[i].quantity / ingredient.unitSize;
+                const ppu = (order.ingredients[i].pricePerUnit * order.ingredients[i].quantity) / quantSold;
+
+                ingredientDisplay.innerText = `${quantSold.toFixed(0)} bottles x $${(ppu / 100).toFixed(2)}`;
+            }else{
+                const convertedQuantity = ingredient.convert(order.ingredients[i].quantity);
+                const convertedPrice = controller.reconvertPrice(order.ingredients[i].ingredient.unitType, order.ingredients[i].ingredient.unit, order.ingredients[i].pricePerUnit);
+
+                ingredientDisplay.innerText = `${convertedQuantity.toFixed(2)} ${ingredient.unit.toUpperCase()} x $${(convertedPrice / 100).toFixed(2)}`;
+            }
 
             ingredientList.appendChild(ingredientDiv);
         }
 
-        document.querySelector("#orderTotalPrice p").innerText = `$${grandTotal.toFixed(2)}`;
+        document.getElementById("orderDetailTotal").innerText = `$${(grandTotal / 100).toFixed(2)}`;
+        document.querySelector("#orderTotalPrice p").innerText = `$${((grandTotal + order.taxes + order.fees) / 100).toFixed(2)}`;
     },
 
     remove: function(order){
@@ -44,7 +59,12 @@ let orderDetails = {
                 if(typeof(response) === "string"){
                     banner.createError(response);
                 }else{
+                    for(let i = 0; i < order.ingredients.length; i++){
+                        order.ingredients[i].quantity = -order.ingredients[i].quantity;
+                    }
+
                     merchant.editOrders([order], true);
+                    merchant.editIngredients(order.ingredients, false, true);
                     banner.createNotification("ORDER REMOVED");
                 }
             })

+ 7 - 2
views/dashboardPage/js/orders.js

@@ -23,6 +23,8 @@ let orders = {
                                 response[i]._id,
                                 response[i].name,
                                 response[i].date,
+                                response[i].taxes,
+                                response[i].fees,
                                 response[i].ingredients,
                                 merchant
                             ));
@@ -79,13 +81,14 @@ let orders = {
             let totalCost = 0;
             
             for(let j = 0; j < merchant.orders[i].ingredients.length; j++){
-                totalCost += merchant.orders[i].ingredients[j].price;
+                const ingredient = merchant.orders[i].ingredients[j];
+                totalCost += ingredient.pricePerUnit * ingredient.quantity;
             }
 
             row.children[0].innerText = merchant.orders[i].name;
             row.children[1].innerText = `${merchant.orders[i].ingredients.length} ingredients`;
             row.children[2].innerText = new Date(merchant.orders[i].date).toLocaleDateString("en-US");
-            row.children[3].innerText = `$${(totalCost / 100).toFixed(2)}`;
+            row.children[3].innerText = `$${((totalCost / 100) + (merchant.orders[i].taxes / 100) + (merchant.orders[i].fees / 100)).toFixed(2)}`;
             row.order = merchant.orders[i];
             row.onclick = ()=>{controller.openSidebar("orderDetails", merchant.orders[i])};
             listDiv.appendChild(row);
@@ -147,6 +150,8 @@ let orders = {
                             response[i]._id,
                             response[i].name,
                             response[i].date,
+                            response[i].taxes,
+                            response[i].fees,
                             response[i].ingredients,
                             merchant
                         );

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

@@ -55,7 +55,17 @@ let transactionDetails = {
                 if(typeof(response) === "string"){
                     banner.createError(response);
                 }else{
-                    merchant.editTransactions(this.transaction, true);
+                    let ingredients = {};
+                    for(let i = 0; i < this.transaction.recipes.length; i++){
+                        let recipe = this.transaction.recipes[i];
+                        for(let j = 0; j < recipe.recipe.ingredients.length; j++){
+                            ingredient = recipe.recipe.ingredients[j];
+
+                            ingredients[ingredient.ingredient.id] = ingredient.quantity * recipe.quantity;
+                        }
+                    }
+
+                    merchant.editTransactions(this.transaction, ingredients, true);
                     banner.createNotification("TRANSACTION REMOVED");
                 }
             })

+ 3 - 4
views/dashboardPage/sidebars/ingredientDetails.ejs

@@ -45,11 +45,10 @@
 
     <div class="lineBorder editRemove"></div>
 
-    <label class="editRemove">RECIPES:
-        <ul id="ingredientRecipeList"></ul>
-    </label>
+    <label class="editRemove">RECIPES:</label>
+
+    <ul id="ingredientRecipeList" class="editRemove"></ul>
 
-    <div class="lineBorder editRemove"></div>
 
     <label class="editAdd" id="displayUnitLabel" style="display: none">DISPLAY UNIT:</label>
 

+ 17 - 1
views/dashboardPage/sidebars/newIngredient.ejs

@@ -52,10 +52,26 @@
             </optgroup>
 
             <optgroup label="OTHER">
-                <option type="other" value="each">each</option>
+                <option type="other" value="each">EACH</option>
+                <option type="other" value="bottle">BOTTLE</option>
             </optgroup>
         </select>
     </label>
 
+    <label id="bottleSizeLabel" style="display: none">BOTTLE SIZE:
+        <input id="bottleSize" type="number" min="0" step="0.01">
+        <select id="bottleUnits">
+            <option value="ml">ML</option>
+            <option value="l">L</option>
+            <option value="tsp">TSP</option>
+            <option value="tbsp">TBSP</option>
+            <option value="ozfl">OZ. FL</option>
+            <option value="cup">CUP</option>
+            <option value="pt">PT</option>
+            <option value="qt">QT</option>
+            <option value="gal">GAL</option>
+        </select>
+    </label>
+
     <button id="submitNewIng" class="button">CREATE</button>
 </div>

+ 18 - 12
views/dashboardPage/sidebars/newOrder.ejs

@@ -16,17 +16,23 @@
 <div id="newOrder">
     <h1>NEW ORDER</h1>
 
-    <label>NAME/ID:
-        <input id="newOrderName" type="text">
-    </label>
-
-    <label>DATE:
-        <input id="newOrderDate" type="date">
-    </label>
-
-    <label>TIME:
-        <input id="newOrderTime" type="time">
-    </label>
+    <div class="newOrderLabels">
+        <label>DATE:
+            <input id="newOrderDate" type="date">
+        </label>
+
+        <label>NAME/ID:
+            <input id="newOrderName" type="text">
+        </label>
+
+        <label>TAXES:
+            <input id="orderTaxes" type="number" min="0" step="0.01">
+        </label>
+
+        <label>OTHER FEES:
+            <input id="orderFees" type="number" min="0" step="0.01">
+        </label>
+    </div>
 
     <div id="selectedIngredientList"></div>
 
@@ -44,7 +50,7 @@
             <div>
                 <input type="number" min="0" step="0.01" placeholder="QUANTITY">
 
-                <input type="number" min="0" step="0.01" placeholder="TOTAL PRICE">
+                <input type="number" min="0" step="0.01" placeholder="PRICE PER UNIT($)">
             </div>
         </div>
     </template>

+ 18 - 2
views/dashboardPage/sidebars/orderDetails.ejs

@@ -19,12 +19,28 @@
 
     <h3 id="orderDetailDate"></h3>
 
-    <h3 id="orderDetailTime"></h3>
-
     <div id="orderIngredients"></div>
 
     <div class="lineBorder"></div>
 
+    <div class="orderDiv">
+        <div class="subOrderDiv">
+            <p>TOTAL:</p>
+            <p id="orderDetailTotal"></p>
+        </div>
+        <div class="subOrderDiv">
+            <P>TAXES:</P>
+            <p id="orderDetailTax"></p>
+        </div>
+        <div class="subOrderDiv">
+            <p>FEES:</p>
+            <p id="orderDetailFee"></p>
+        </div>
+        
+    </div>
+
+    <div class="lineBorder"></div>
+
     <div id="orderTotalPrice">
         <h3>GRAND TOTAL</h3>
         <p></p>

+ 34 - 2
views/dashboardPage/sidebars/sidebars.css

@@ -224,6 +224,9 @@ Ingredient Details
     }
 
     #ingredientDetails label{
+        display: flex;
+        flex-direction: column;
+        align-items: center;
         font-size: 20px;
         text-align: center;
         margin: 0;
@@ -267,10 +270,11 @@ Ingredient Details
         }
 
     #ingredientRecipeList{
+        text-align: center;
         list-style: none;
         overflow: auto;
         max-height: 150px;
-        width: 90%;
+        width: 100%;
         margin-left: auto;
     }
 
@@ -279,10 +283,11 @@ Ingredient Details
         padding: 10px;
         border-radius: 5px;
         font-weight: bold;
+        width: 75%;
         background: rgb(240, 252, 255);
         border: 1px solid black;
         cursor: pointer;
-        margin: 1px 0;
+        margin: 1px auto;
     }
 
         #ingredientRecipeList > li:hover{
@@ -440,6 +445,24 @@ Add Recipe
     width: 40%;
 }
 
+    .newOrderLabels{
+        display: flex;
+        flex-direction: column;
+        width: 100%;
+        margin-bottom: 25px;
+    }
+
+        .newOrderLabels label{
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            font-size: 15px;
+        }
+
+        .newOrderLabels input{
+            width: 50%;
+        }
+
     #newOrderIngredients{
         display: flex;
         flex-direction: column;
@@ -559,6 +582,15 @@ Add Recipe
     width: 100%;
 }
 
+    .orderDiv{
+        width: 50%;
+    }
+
+        .subOrderDiv{
+            display: flex;
+            justify-content: space-between;
+        }
+
     #orderIngredients{
         width: 90%;
         overflow: auto;

+ 0 - 10
views/informationPage/help.js

@@ -1,10 +0,0 @@
-window.helpObj = {
-    display: function(){
-        document.getElementById("legalStrand").style.display = "none";
-        document.getElementById("helpStrand").style.display = "flex";
-
-        let button = document.getElementById("logInButton");
-        button.innerText="LEGAL";
-        button.onclick = ()=>{legalObj.display()};
-    }
-}

+ 0 - 48
views/informationPage/information.css

@@ -1,48 +0,0 @@
-.strand{
-    justify-content: center;
-}
-
-    .strand{
-        margin: 50px;
-    }
-
-.content{
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    border: 2px solid black;
-    box-shadow: -5px -5px 5px rgb(0, 27, 45) inset;
-    padding: 30px;
-    text-align: justify;
-    max-width: 800px;
-    max-height: 65vh;
-    overflow-y: auto;
-}
-
-.content > *{
-    margin: 15px;
-    padding-bottom: 25px;
-}
-
-ol{
-    list-style: decimal inside none;
-}
-
-    ol > *{
-        margin: 10px;
-    }
-
-/* Legal Strand */
-#legalStrand > *{
-    margin: 15px;
-}
-
-/* Help Strand */
-#helpStrand{
-    flex-direction: column;
-    align-items: center;
-}
-
-    #helpStrand > *{
-        margin: 15px;
-    }

+ 0 - 496
views/informationPage/information.ejs

@@ -1,496 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-    <head>
-        <meta charset="utf-8">
-        <title>The Subline</title>
-        <link rel="icon" type="img/png" href="/shared/images/logo.png">
-        <link rel="stylesheet" href="/informationPage/information.css">
-        <link rel="stylesheet" href="/shared/shared.css">
-    </head>
-    <body>
-        <% include ../shared/header %>
-
-        <strand-selector></strand-selector>
-
-        <div id="legalStrand" class="strand">
-            <div class="content">
-                <h1>The Subline, LLC Privacy Policy</h1>
-            
-                <p>Last modified: December 23, 2019</p>
-            
-                <h3>Introduction</h3>
-            
-                <p>The Subline, LLC. ("Company" or "We") respects your privacy, and we are committed to protecting it through our compliance with this policy.</p>
-            
-                <p>This policy describes the types of information we may collect from you or that you may provide when you visit the website, www.thesubline.com (the "Website") or utilize any of the services on our platform, including our practices for collecting, using, maintaining, protecting, and disclosing that information.</p>
-            
-                <p>This policy applies to information we collect:</p>
-            
-                <ol>
-                    <li>On our Website and our Platform;</li>
-            
-                    <li>In emails, texts, and other electronic messages between you and the Website and our Platform in connection with inventory management (procurement, sales, management, etc.);</li>
-            
-                    <li>When you interact with our advertising and applications on third-party websites and services, if those applications or advertising include links to this policy.</li>
-                </ol>
-            
-                <p>Please read this policy carefully to understand our policies and practices regarding your information and how we will treat it. If you do not agree with our policies and practices, your choice is not to use our Website or Platform. By accessing or using our Website and Platform, you agree to this privacy policy. This policy may change from time to time (see Changes to Our Privacy Policy). Your continued use of our Website and the Platform after we make changes is deemed to be acceptance of those changes, so please check the policy periodically for updates.</p>
-            
-                <h3>Information We Collect About You and How We Collect It</h3>
-            
-                <p>Generally, no personally identifiable information, such as your name or mailing address, is collected anytime you visit the Website/Platform, nor is it required for you to visit the Website and our Platform. We collect several types of information from and about users of our Website and our Platform, including information:</p>
-            
-                <ol>
-                    <li>by which you may be personally identified, such as name, postal address, e-mail address, credit card information, or telephone number ("personal information");</li>
-            
-                    <li>about your internet connection, the equipment you use to access our Website and our Platform and usage details.</li>
-                </ol>
-            
-                <h3>We collect this information:</h3>
-            
-                <ol>
-                    <li>Directly from you when you provide it to us;</li>
-            
-                    <li>Automatically as you navigate through the site. Information collected automatically may include usage details, IP addresses, and information collected through cookies;</li>
-            
-                    <li>From third parties, for example, our business partners.</li>
-                </ol>
-            
-                <h3>Information You Provide to Us</h3>
-            
-                <p>When personal information is collected from you, such as your name and email address, we generally let you know at the time of collection how we will use the personal information. The Company will not share or sell the personal information you provide to us with anyone for their marketing use. We may share the personal information you provide with other companies we have hired to provide services for us. Your information will only be used by these third parties (or their subcontractors) to perform the assigned function.</p>
-            
-                <p>The information we collect on or through our Website and our Platform may include:</p>
-            
-                <ol>
-                    <li>Information that you provide by filling in forms on our Website and our Platform. This includes information provided at the time of registering or requesting further services. We may also ask you for information when you enter a contest or promotion sponsored by us, and when you report a problem with our Website or Platform.</li>
-            
-                    <li>Records and copies of your correspondence (including email addresses), if you contact us.</li>
-            
-                    <li>Your responses to surveys that we might ask you to complete for research purposes.</li>
-                </ol>
-            
-                <p>You also may provide information to be published or displayed (hereinafter, "posted") on public areas of the Website and our Platform, or transmitted to other users of the Website and our Platform or third parties (collectively, "User Contributions"). Your User Contributions are posted on and transmitted to others at your own risk. We cannot control the actions of other users of the Website and our Platform with whom you may choose to share your User Contributions. Therefore, we cannot and do not guarantee that your User Contributions will not be viewed by unauthorized persons.</p>
-            
-                <h3>Information We Collect Through Automatic Data Collection Technologies</h3>
-            
-                <p>As you navigate through and interact with our Website and our Platform, we may use automatic data collection technologies to collect certain information about your equipment, browsing actions, and patterns, including:</p>
-            
-                <ol>
-                    <li>Details of your visits to our Website/Platform, including traffic data, location data, logs, and other communication data and the resources that you access and use on the Website/Platform;</li>
-            
-                    <li>Information about your computer and internet connection, including your IP address, operating system, and browser type.</li>
-                </ol>
-            
-                <p>The information we collect automatically is statistical data and does not include personal information, but we may maintain it or associate it with personal information we collect in other ways or receive from third parties. It helps us to improve our Website and our Platform and to deliver a better and more personalized service, including by enabling us to:</p>
-            
-                <ol>
-                    <li>Estimate our audience size and usage patterns.</li>
-            
-                    <li>Store information about your preferences, allowing us to customize our Website and our Platform according to your individual interests.</li>
-            
-                    <li>Speed up your searches.</li>
-            
-                    <li>Recognize you when you return to our Website and our Platform.</li>
-                </ol>
-            
-                <p>The technologies we use for this automatic data collection may include:</p>
-            
-                <ol>
-                    <li>Cookies (or browser cookies). A cookie is a small file placed on the hard drive of your computer. You may refuse to accept browser cookies by activating the appropriate setting on your browser. However, if you select this setting you may be unable to access certain parts of our Website. Unless you have adjusted your browser setting so that it will refuse cookies, our system may issue cookies when you direct your browser to our Website and our Platform.</li>
-            
-                    <li>Flash Cookies. Certain features of our Website and our Platform may use local stored objects (or Flash cookies) to collect and store information about your preferences and navigation to, from, and on our Website and our Platform. Flash cookies are not managed by the same browser settings as are used for browser cookies. For information about managing your privacy and security settings for Flash cookies, see Choices About How We Use and Disclose Your Information.</li>
-            
-                    <li>Web Beacons. Pages of our Website and our Platform and our e-mails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or and for other related website statistics (for example, recording the popularity of certain website content and verifying system and server integrity).</li>
-                </ol>
-            
-                <p>We do not collect personal information automatically, but we may tie this information to personal information about you that we collect from other sources or you provide to us.</p>
-            
-                <h3>How We Use Your Information</h3>
-            
-                <p>We use information that we collect about you or that you provide to us, including any personal information:</p>
-            
-                <ol>
-                    <li>To present our Website and our Platform and its contents to you;</li>
-            
-                    <li>To provide you with information, products, or services that you request from us;</li>
-            
-                    <li>To fulfill any other purpose for which you provide it;</li>
-            
-                    <li>To carry out our obligations and enforce our rights arising from any contracts entered into between you and us, including for billing and collection;</li>
-            
-                    <li>To notify you about changes to our Website and our Platform or any products or services we offer or provide though it;</li>
-            
-                    <li>To allow you to participate in interactive features on our Website and our Platform or participate in promotions, reward programs or other similar programs;</li>
-            
-                    <li>In any other way we may describe when you provide the information;</li>
-            
-                    <li>For any other purpose with your consent.</li>
-                </ol>
-            
-                <h3>Disclosure of Your Information</h3>
-            
-                <p>We may disclose aggregated information about our users, and information that does not identify any individual, without restriction. We may disclose personal information that we collect or you provide as described in this privacy policy:</p>
-            
-                <ol>
-                    <li>To our subsidiaries and affiliates.</li>
-            
-                    <li>To contractors, service providers, and other third parties we use to support our business and who are bound by contractual obligations to keep personal information confidential and use it only for the purposes for which we disclose it to them.</li>
-            
-                    <li>To a buyer or other successor in the event of a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Company's assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which personal information held by Company about our Website users is among the assets transferred.</li>
-            
-                    <li>For any other purpose disclosed by us when you provide the information.</li>
-            
-                    <li>With your consent.</li>
-                </ol>
-            
-                <p>We may also disclose your personal information:</p>
-            
-                <ol>
-                    <li>To comply with any court order, law, or legal process, including to respond to any government or regulatory request.</li>
-            
-                    <li>To enforce or apply our terms of use or terms of sale and other agreements, including for billing and collection purposes.</li>
-            
-                    <li>If we believe disclosure is necessary or appropriate to protect the rights, property, or safety of Company, our customers, or others. This includes exchanging information with other companies and organizations for the purposes of fraud protection and credit risk reduction.</li>
-                </ol>
-            
-                <h3>Choices About How We Use and Disclose Your Information</h3>
-            
-                <p>We strive to provide you with choices regarding the personal information you provide to us. We have created mechanisms to provide you with the following control over your information:</p>
-            
-                <ol>
-                    <li>Tracking Technologies and Advertising. You can set your browser to refuse all or some browser cookies, or to alert you when cookies are being sent. To learn how you can manage your Flash cookie settings, visit the Flash player settings page on Adobe's website. If you disable or refuse cookies, please note that some parts of this site may then be inaccessible or not function properly.</li>
-                </ol>
-            
-                <h3>Accessing and Correcting Your Information</h3>
-            
-                <p>You may also send us an email at the address shown below to request access to, correct or delete any personal information that you have provided to us. We cannot delete your personal information except by also deleting your user account. We may not accommodate a request to change information if we believe the change would violate any law or legal requirement or cause the information to be incorrect.</p>
-            
-                <h3>Your California Privacy Rights</h3>
-            
-                <p>California Civil Code Section § 1798.83 permits users of our Website and our Platform that are California residents to request certain information regarding our disclosure of personal information to third parties for their direct marketing purposes. To make such a request, please send an email to info@thesubline.com or write us at the address noted below.</p>
-            
-                <h3>Data Security</h3>
-            
-                <p>We have implemented measures designed to secure your personal information from accidental loss and from unauthorized access, use, alteration, and disclosure. All information you provide to us is stored on our secure servers behind firewalls. Any payment transactions will be encrypted using SSL technology.</p>
-            
-                <p>The safety and security of your information also depends on you. Where we have given you (or where you have chosen) a password for access to certain parts of our Website and our Platform, you are responsible for keeping this password confidential. We ask you not to share your password with anyone.</p>
-            
-                <p>Unfortunately, the transmission of information via the internet is not completely secure. Although we do our best to protect your personal information, we cannot guarantee the security of your personal information transmitted to our Website and our Platform. Any transmission of personal information is at your own risk. We are not responsible for circumvention of any privacy settings or security measures contained on the Website and our Platform.</p>
-            
-                <h3>Changes to Our Privacy Policy</h3>
-            
-                <p>It is our policy to post any changes we make to our privacy policy on this page. If we make material changes to how we treat our users' personal information, we will notify you through a notice on the Website home page. The date the privacy policy was last revised is identified at the top of the page. You are responsible for ensuring we have an up-to-date active and deliverable email address for you, and for periodically visiting our Website and our Platform and this privacy policy to check for any changes.</p>
-            
-                <h3>Miscellaneous</h3>
-            
-                <p>Disclaimer Notice. We make no claims, promises or guarantees about the accuracy, currency, completeness or adequacy of the content of the Website and our Platform, products and services available through the Website and our Platform, and expressly disclaim liability for errors and omissions in the related content. No warranty of any kind, implied, express or statutory, including but not limited to warranties of non-infringement of third-party rights, title, merchantability, fitness for a particular purpose and freedom from computer virus is given with respect to the Website and our Platform, products and services offered through the Website and our Platform. Although we have put in place security systems (as described in this Privacy Policy) that are designed to prevent unauthorized disclosure of your Personal Information, due to the nature of internet technologies we cannot provide assurances as to the security of this information, and expressly disclaim any such obligation.</p>
-            
-                <p>Choice of Law and Jurisdiction. This Privacy Policy shall be construed in accordance with the laws of the State of Florida, without regard to any conflict of law provisions. Any dispute arising under this Privacy Policy shall be resolved exclusively by the state or federal courts sitting in the judicial district that includes Seattle, Washington.</p>
-            
-                <h3>Contact Information</h3>
-            
-                <p>To ask questions or comment about this privacy policy and our privacy practices, contact us at:</p>
-            
-                <p class="contactInfo">The Subline</p>
-            
-                <p class="contactInfo">ATTN: PRIVACY POLICY</p>
-            
-                <p class="contactInfo">info@thesubline.com</p>
-            
-                <p class="contactInfo last">www.thesubline.com</p>
-            </div>
-
-            <div class="content">
-                <h1>The Subline, Terms and Conditions</h1>
-
-                <h4>Last update: January 8, 2020</h4>
-
-                <p>These terms and conditions specify the terms of the contract between The Subline and the Merchant Venue named in the Sign-Up Form (&quot;Merchant Venue&quot;), pursuant to which the Merchant Venue may use the The Subline service. If the Merchant Venue does not accept these terms and conditions (including following any amendment to these terms and conditions), the Merchant Venue must immediately cease use of the Service and notify The Subline in writing.</p>
-
-                <p>By using the Service, the Merchant Venue agrees to be legally bound by the Agreement (as varied from time to time), and to comply with all The Subline policies, including the The Subline Privacy Policy (both of which are located at hazInut.com/privacy-policy/,</p>
-
-                <p>1. General</p>
-
-                <p>1.1 The Service provides an inventory management solution for food and beverage providers using Clover devices. Information and analysis provided by The Service is available on a personalized and private desktop browser-based dashboard.</p>
-
-                <p>1.2 Much of the analysis completed by The Service is based on Merchant Information stored on Clover servers. After successful login (granted by Clover) to the Merchant Dashboard, The Service will pull information from the Clover servers, update the information, and display it to the Merchant.</p>
-
-                <p>1.3 This Agreement specifies the terms on which The Subline makes available, and Customer will use, the Service.</p>
-
-                <p>2. Definitions and Interpretation</p>
-
-                <p>2.1 In this Agreement:</p>
-
-                <p>&quot;Agreement&quot; means this Merchant Venue Agreement (as amended or updated from time to time), including the Subscription Plan and these terms and conditions.</p>
-
-                <p>&quot;The Subline Service Fee&quot; means the monthly Fee payable by the Merchant Venue to The Subline for the Service as detailed in the Subscription Plan.</p>
-
-                <p>&quot;HazInut Service Fee Start Date&quot; means the first date from which the The Subline Service Fee is charged to the Merchant Venue, as detailed in the Subscription Plan.</p>
-
-                <p>&quot;Chargeback&quot; means a demand by a bank or other credit-card provider, or other provider of electronic payment facilities, for a merchant to make good the loss on a fraudulent or disputed transaction.</p>
-
-                <p>&quot;Customer&quot; means anyone who communicates an Order, in person or using third-party applications to the Merchant Venue for the supply of Venue Products.</p>
-
-                <p>&quot;Menu&quot; means a listing of Venue Products that the Merchant Venue will sell to Customers (including the price a Customer must pay to purchase each item of Venue Products) that The Subline will publish to, and process Orders from, Customers by means of the Service Portals.</p>
-
-                <p>&quot;Order&quot; means an offer from a Customer to Merchant Venue to purchase Venue Products for the Order Price.</p>
-
-                <p>&quot;Order Price&quot; means the price a Customer will pay to Merchant Venue for the supply of Venue Products (exclusive of GST and any other applicable taxes), which must be calculated in accordance with pricing specified on the Merchant Venue's Menu.</p>
-
-                <p>&quot;Merchant Venue&quot; means an establishment that utilizes The Subline for Inventory Management.</p>
-
-                <p>&quot;Merchant Venue Material&quot; includes any material in which Merchant Venue owns or holds intellectual property rights, or which commercially identifies, or can be used to commercially identify, the Merchant Venue, including its name, logos, trademarks, slogans, Menu, names of Venue Products, pricing and location.</p>
-
-                <p>&quot;Point of Supply&quot; means the time and location at which Merchant Venue supplies Venue Products to a Customer, which may include the Merchant Venue's business premises (for Venue Products collected by the Customer).</p>
-
-                <p>&ldquo;Merchant Purchase&rdquo; means any purchase a raw material (food, beverage, condiments, utensils, plates, etc) that the Merchant sells to the Customer as a routine part of the Merchant&rsquo;s business.</p>
-
-                <p>&quot;Venue Products&quot; means any product that Merchant Venue supplies, or makes available for supply, to any Customer, including any type of food or beverage.</p>
-
-                <p>&quot;Service&quot; means the provision of online facilities to enable Merchants to view real-time inventory status, Orders, Merchant Purchases and the analytics provided.</p>
-
-                <p>&quot;Service Portals&quot; means facilities by which The Subline makes the Service available to Merchants from time to time.</p>
-
-                <p>&quot;Sign Up Form&quot; means a document with the name, address and email of the Merchant Venue that is completed, usually at the time of the Merchant Venue agrees to use The Subline.</p>
-
-                <p>&quot;Subscription Plan' means the terms of purchase agreed by Merchant Venue via the channel used to acquire The Subline including but not limited to The Subline recurring Fee, currency of the The Subline Service Fee and The Subline Service Fee start date.</p>
-
-                <p>&quot;Other Conditions&quot; means any additional terms and conditions agreed to by a Merchant venue for services other than the standard ones, and via additional side agreements,</p>
-
-                <p>2.2 In this Agreement:</p>
-
-                <p>a. headings to and within clauses are for convenience and reference only and do not form a part of this Agreement and shall not in any way affect the interpretation of this Agreement;</p>
-
-                <p>b. Words importing the singular include the plural and vice versa; and</p>
-
-                <p>c. All monetary references are the currency detailed in the Subscription Plan otherwise specified.</p>
-
-                <p>2.3 This Agreement incorporates any Other Conditions, and in the event of any inconsistency between any of these terms and conditions and any Other Conditions, the Other Conditions will prevail to the extent of the inconsistency.</p>
-
-                <p>3. Service and availability</p>
-
-                <p>3.1 The Subline is the owner and operator of the Service Portals. The Service Portals allows Merchants to monitor their inventory and receive information from The Service.</p>
-
-                <p>3.2 The Subline reserves the right to provide the Services on other Service Portals in the future, without notice to the Merchant Venue.</p>
-
-                <p>3.3 The Merchant Venue operates one or more Venue(s), at which the Venue Products are prepared and sold to the public.</p>
-
-                <p>3.4 The Subline will:</p>
-
-                <p>a. make the Service available for use by the public through the Service Portals;</p>
-
-                <p>b. provide current Inventory counts based on information provided by the Merchant;</p>
-
-                <p>3.5 The Merchant Venue acknowledges that The Subline has no responsibility for the fulfilment of Orders of Raw Materials or Ingredients; The Subline does not hold any Collected Payments.</p>
-
-                <p>3.6 The Subline may without notice make changes to or temporarily suspend the operation of the Service Portals should The Subline deem this necessary.</p>
-
-                <p>4. Obligations of The Subline</p>
-
-                <p>4.1 Subject to clause 4.2 below, The Subline will:</p>
-
-                <p>a. list the Merchant Venue and publish its Menu by means of the Service Portals;</p>
-
-                <p>b_ communicate Orders to the Merchant Venue; and</p>
-
-                <p>c. provide reporting on Orders processed via the service.</p>
-
-                <p>4.2 The Subline will use reasonable endeavors to accurately display on the Service Portals information relevant for the Merchants provided by the Merchant or by POS services.</p>
-
-                <p>4.3 The Subline will work with the Merchant to provide quality information and data that The Subline reasonably requires in order to supply the Service.</p>
-
-                <p>5. Obligations of the Merchant Venue</p>
-
-                <p>5.1 The Merchant Venue is responsible for ensuring that at all times it has a functional telephone and, where agreed with The Subline, a facsimile machine, or means of receiving electronic communication such as email, in order to provide updates to the Service.</p>
-
-                <p>5.2 The Merchant Venue must as promptly as possible:</p>
-
-                <p>a. review each Order communicated to it by The Subline;</p>
-
-                <p>b. communicate Order Acceptance to Customers; and</p>
-
-                <p>c. execute Orders promptly and in accordance with a Customer's reasonable expectations.</p>
-
-                <p>5.3 The Merchant Venue acknowledges that:</p>
-
-                <p>a. no contract exists, or at any time will exist, between The Subline and the Merchant for the supply of Raw Materials or Ingredients;</p>
-
-                <p>b. that the sole and exclusive obligation to purchase Raw Materials and Inventory is owed by the Merchant Venue;</p>
-
-                <p>c. the Merchant Venue indemnifies The Subline against any claim or demand made or cost, loss or liability suffered by the Merchant arising directly or indirectly from wasted or lost Raw Materials or Inventory;</p>
-
-                <p>5.5 The Merchant Venue must ensure that the information it provides to The Subline is current and correct, including, but not limited to, its company and/or business name, address, contact telephone number, manager/contact person details, and other relevant information, and the Merchant Venue agrees that it:</p>
-
-                <p>a. must immediately notify The Subline if any information it is required to supply in accordance with this clause changes or becomes inaccurate or incorrect; and</p>
-
-                <p>b. will indemnify The Subline against any claim, loss, liability or damage arising out of any error or inaccuracy of, or any delay in notifying The Subline of any change to, any of the information it is required to supply in accordance with this clause.</p>
-
-                <p>5.6 The Subline reserves the right to:</p>
-
-                <p>a. regularly carry out inspections to ensure compliance with this clause 5; and</p>
-
-                <p>b. immediately suspend or terminate (at The Subline's sole discretion) the supply of the Service if The Subline reasonably suspects the Merchant Venue has not complied with any requirement of this clause 5.</p>
-
-                <p>6. License to use Merchant Venue Material</p>
-
-                <p>6.1 The Merchant Venue grants to The Subline an unrestricted, Worldwide, royalty-free license during the term of this Agreement to use, Venue Products, modify and adapt all Merchant Venue Material for the purposes of:</p>
-
-                <p>a. inclusion on the Service Portals and as may be otherwise required for the proper supply of the Services; and</p>
-
-                <p>b. the general promotion of the Service and Merchant Venue, including without limitation:</p>
-
-                <p>i. use of the Venue's Name for internet advertising purposes, including Google Adwords, to support advertising campaigns and domain registrations for the Service Portals and The Subline; and</p>
-
-                <p>ii. subject to clause 6.4 or unless otherwise agreed in writing, the registration in the United States of America or any other jurisdiction of domain names incorporating any part of the Merchant Venue Material, or material similar to the Venue Material.</p>
-
-                <p>iii. the right to register on google maps, google locations or any other third-party directory websites or services.</p>
-
-                <p>6.2 For the avoidance of doubt, unless otherwise stated or the parties otherwise agreed in writing, the Merchant Venue acknowledges and agrees that, subject to clause 6.4, The Subline may use Merchant Venue Material to register a domain name and operate a website in such a way that a Customer may reasonably assume such website is operated by or on behalf of the Merchant Venue, including by using a domain name the same or similar to the business name of the Merchant Venue and diverting website traffic to a Service Portal, and the Merchant Venue explicitly authorizes the operating of such website by The Subline for the purpose of promoting the sale of Venue</p>
-
-                <p>Products using the Service.</p>
-
-                <p>6.3 Any material the Merchant Venue transmits or submits to The Subline either through the Service Portals or otherwise (&quot;Communicated Material'') shall be considered and may be treated by The Subline as non-confidential, subject to The Subline's obligations under relevant legislation. The Merchant Venue grants to The Subline a royalty-free, perpetual, irrevocable, non-exclusive license to use, copy, modify, adapt, translate, publish and distribute world-wide any Communicated Material for the purposes of providing services under this Agreement or to or for the purposes of advertising and promotion of the Service Portals. The Merchant Venue agrees that all information provided to The Subline that is published, may be relied upon and viewed by Customers to enable them to make decisions and form a legally binding contract with the Merchant Venue.</p>
-
-                <p>7. Operational Information</p>
-
-                <p>7.1 The Merchant Venue is responsible for updating/maintaining Recipes, Inventory Stores, and Inventory Purchases, and any other data inputs that the Service requires. If the Merchant has questions or requires clarification about the The Service, the Merchant must reach out to The Subline for assistance.</p>
-
-                <p>8. Payment</p>
-
-                <p>8.1 The Merchant Venue must pay The Subline the &ldquo;The Subline Service Fee&rdquo; set out in the Subscription Plan.</p>
-
-                <p>8.2 The Subline Service Fee be payable on a monthly basis starting from the &ldquo;The Subline Service Fee&rdquo; Start Date.</p>
-
-                <p>8.3 The merchant venue agrees to provide payment details to The Subline or the channel via which The Subline services was signed up, for direct deduction of all necessary The Subline Service Fees.</p>
-
-                <p>9. Confidentiality</p>
-
-                <p>9.1 A party will not, except with the written consent of the other party or where required to do so by law or stock exchange regulation, disclose any confidential information of the other party.</p>
-
-                <p>9.2 Confidential information of The Subline includes without limitation its business strategies, pricing, revenues, expenses, and order information.</p>
-
-                <p>9.3 Both parties agree to treat as strictly confidential the contents of the Subscription Plan and all other information, data and facts that may be shared between both parties during the course of this Agreement.</p>
-
-                <p>10. Warranty and Indemnity</p>
-
-                <p>10.1 The Merchant Venue warrants that if the Merchant Venue ceases business, closes operations for a material period or is otherwise unable to offer Venue Products to Customers or to satisfy any obligation to Customers, Merchant Venue will immediately inform The Subline.</p>
-
-                <p>10.2 The Subline does not guarantee or warrant that the Service Portals, software, hardware or services will be free from defects or malfunctions. If errors occur, The Subline will use its best endeavors to resolve these as quickly as possible.</p>
-
-                <p>10.3 The Merchant Venue indemnifies and holds harmless The Subline (and its directors, officers, agents, representatives and employees) from and against any and all claims, suits, liabilities, judgments, losses and damages arising out of or in connection with any claim or suit or demand:</p>
-
-                <p>a. arising from any failure to provide notice in accordance with clause 10.1;</p>
-
-                <p>b. by a Customer (or any party on whose behalf a Customer has been acting) in respect of, arising out of, or in connection with, Services;</p>
-
-                <p>c. in any way associated with Venue Products;</p>
-
-                <p>d. compliance with food quality laws or regulations; or</p>
-
-                <p>e. any relevant liquor licensing laws or regulations, except and to the extent any liability, loss or damage arises from the reckless or malicious act or omission of The Subline.</p>
-
-                <p>11. Term and Termination</p>
-
-                <p>11.1 This Agreement starts on the date the Merchant Venue downloads the The Subline Venue Application and accepts these terms and unless terminated earlier under this clause will continue indefinitely.</p>
-
-                <p>11.2 Either party may terminate this Agreement for convenience without giving cause at any time upon 10 days prior written notice to the other party. Following any such termination for convenience:</p>
-
-                <p>a. the Merchant Venue's obligations under this Agreement will continue until the end of the month during which notice is given; and</p>
-
-                <p>b. the Merchant Venue's online listing and promotion undertaken by The Subline will be ceased as soon as is practicable.</p>
-
-                <p>11.3 The Subline may terminate this Agreement, with immediate effect if the Merchant Venue:</p>
-
-                <p>a. provides any inaccurate information about its business to The Subline, such as inaccurate information relating to opening hours, delivery areas, delivery terms or prices;</p>
-
-                <p>b. fails to deliver an Accepted Order to any Customer (except where the Merchant Venue demonstrates it reasonably believed the Accepted Order was fraudulent or the Customer did not intend to, or refused to pay, the Order Price); or</p>
-
-                <p>c. is subject to any event of insolvency (such as the appointment of an administrator, receiver or liquidator or fails to pay its debts as and when they fall due) or bankruptcy (such as having bankruptcy proceedings commenced against the Merchant Venue or being unable to pay any of its creditors).</p>
-
-                <p>11.4 Either party may terminate this Agreement with immediate effect by notice in writing to the other party if the other party commits a material breach of this Agreement not capable of remedy, or in the case of a material breach capable of remedy, is not remedied within 3 business days after written notice is given to the breaching party, specifying the breach.</p>
-
-                <p>11.5 Termination of this Agreement shall not affect the accrued rights or liabilities of the parties at the date of termination.</p>
-
-                <p>12. Limitation of Liability</p>
-
-                <p>12.1 Except for liability in relation to breach of any Non-excludable Condition and liability under clause 12.3, The Subline's total liability to the Merchant Venue in contract, including for one or more breaches of any express term or terms (including any indemnity) of this Agreement (in aggregate), tort (including in negligence), statute, or otherwise, is limited to an amount equal to the total amount of The Subline Service Fee paid by the Merchant Venue to The Subline under this Agreement during the 3 month period before the liability arose.</p>
-
-                <p>12.2 The Subline's total liability to the Merchant Venue for a breach of any Non-excludable Condition (other than a Non-excludable Condition that by law cannot be limited) is limited, at The Subline's option to any one of resupplying, replacing or repairing, or paying the cost of resupplying, replacing or repairing the goods in respect of which the breach occurred, or supplying again or paying the cost of supplying again, the services in respect of which the breach occurred.</p>
-
-                <p>12.3 Except for liability in relation to breach of any Non-excludable Condition, The Subline excludes all liability to the Merchant Venue for lost profits, lost revenue, lost savings, lost business, loss of opportunity, lost data or any consequential or indirect loss arising out of, or in connection with, any services (including the Service), and any claims by any third person (including any Customer), or this Agreement, even if:</p>
-
-                <p>a. The Subline knew that loss was possible; or</p>
-
-                <p>b. the loss was otherwise foreseeable.</p>
-
-                <p>12.4 The Merchant Venue acknowledges that The Subline may make facilities available on the Service Portals for the access by Customers to ratings and reviews of suppliers of goods and services, which may include reviews or ratings of the Merchant Venue, and The Subline will have no liability to the Merchant Venue or any other person for any reason whatsoever arising from any comment, review, assessment or statement (whether true or untrue) made or published by any third person about the Merchant Venue or any person or entity associated with the Merchant Venue.</p>
-
-                <p>13. Dispute resolution</p>
-
-                <p>13.1 Any dispute arising in connection with this Agreement must be handled in accordance with this clause before a party may commence any form of litigation or legal proceedings.</p>
-
-                <p>13.2 A party must give notice to the other party in writing of the nature of any dispute, and within 5 days of such notice:</p>
-
-                <p>a. each party must appoint a representative with full decision making authority to negotiate on behalf of, and bind, their party to resolution of the dispute, and those representatives must meet personally (or, if agreed, by telephone, video conference or such other means as the parties consider appropriate) to consider and seek to resolve the dispute within 5 days of their appointment;</p>
-
-                <p>b. if the respective representatives are unable to resolve the dispute after 5 days of their first meeting (or other such period as is agreed between the parties), refer the dispute to the respective chief executive officers (or equivalent) of each party, who must meet personally (or, if agreed, by telephone, video conference or such other means as the parties consider appropriate) within 7 days to discuss and seek to resolve the dispute; and</p>
-
-                <p>c. if the respective chief executive officers (or equivalent) are unable to resolve the dispute within 7 days of their first meeting, either party is free to commence such process, including alternative dispute resolution or litigation, as they see fit to resolve the dispute.</p>
-
-                <p>14. Variation</p>
-
-                <p>14.1 These terms and conditions may be amended by The Subline at any time by posting revised terms and conditions online at The Subline.com/merchant-terms/, and, subject to clause 18.2, those amended terms and conditions will be effective immediately on posting, and by continuing to use the Service the Merchant Venue will be deemed to have accepted the amended terms and conditions.</p>
-
-                <p>14.2 If the Merchant Venue does not accept any variation to these terms and conditions, it may terminate the Agreement with immediate effect, provided that the Merchant Venue gives notice in writing of such termination to The Subline within 7 days of the amended terms and conditions becoming effective.</p>
-
-                <p>14.3 This Agreement and the Subscription Plan may be amended by The Subline at any time by giving notice in writing (&quot;Amendment Notice&quot;) of the amended terms to the Merchant Venue, and subject to clause 18.4, those amended terms will be effective immediately from the date of the Amendment Notice. Unless the Merchant Venue terminates the Agreement within 7 days after the date of the Amendment Notice in accordance with clause 18.4, the Merchant Venue will be deemed to have accepted the amended terms. If the Merchant Venue terminates the Agreement in accordance with clause 18.4, the amendments detailed in the Amendment Notice will not be binding on the Merchant Venue.</p>
-
-                <p>14.4 If the Merchant Venue does not accept any variation to the Agreement or Subscription Plan, it may terminate the Agreement with immediate effect, provided that the Merchant Venue gives notice in writing of such termination to The Subline within 7 days of the amended terms becoming effective.</p>
-
-                <p>14.5 Except where otherwise explicitly permitted by a clause of this Agreement, the provisions of this Agreement may not be varied by the Merchant Venue, except by agreement in writing signed by the parties.</p>
-
-                <p>15. General</p>
-
-                <p>15.1 For the purposes of this Agreement, any notice required to be given in writing may be given by electronic means, including by email or such other form of written communication as the parties agree from time to time.</p>
-
-                <p>15.2 This Agreement contains all the terms agreed between the parties regarding its subject matter and supersedes and excludes any prior agreement, understanding or arrangement between the parties, whether oral or in writing. No representation, undertaking or promise shall be taken to have been given or be implied from anything said or written in negotiations between the parties prior to this Agreement except as expressly stated in this Agreement.</p>
-
-                <p>15.3 The failure of either party to assert any of its rights under the Agreement, including, but not limited to, the right to terminate the Agreement in the event of breach or default by the other party, will not be deemed to constitute a waiver by that party of its right thereafter to enforce each and every provision of this Agreement in accordance with their terms.</p>
-
-                <p>15.4 The invalidity or unenforceability of any term of or right arising pursuant to this Agreement shall not adversely affect the validity or enforceability of the remaining terms and rights.</p>
-
-                <p>15.5 The Merchant Venue must not assign, transfer, charge or otherwise encumber, create any trust over or deal in any manner with this Agreement or any right, benefit or interest under it, nor transfer, novate or sub-contract any of Merchant Venue's obligations under it.</p>
-
-                <p>15.6 The Subline may assign or novate part or all of this Agreement to any party at any time, and the Merchant Venue:</p>
-
-                <p>a. consents to the transfer or disclosure of its personal Information and this Agreement to any purchaser of the business of The Subline or its assets if that outcome occurs;</p>
-
-                <p>b. hereby acknowledges its consent to such assignment or novation to any party; and</p>
-
-                <p>c. agrees to do all things reasonably required by The Subline, including executing an appropriate deed of assignment or novation, as The Subline reasonably requires to give full effect to such assignment or novation.</p>
-
-                <p>15.7 This Agreement does not create any agency, employment, partnership, joint venture, or other joint relationship between The Subline and the Merchant Venue. The Subline and the Merchant Venue are independent contractors and neither has any authority to bind the other. For the avoidance of doubt, The Subline has no authority to bind the Merchant Venue to any contract with a Customer, and no contract for the supply of Venue Products is formed between any party until the Merchant Venue communicates Order Acceptance, at which time a contract is formed solely between the Merchant Venue and the Customer.</p>
-
-                <p>15.8 This Agreement will be governed by and construed in according to the law of the State of Florida, USA, and each party submits unconditionally to the jurisdiction of the courts of that State.</p>
-
-                <p>16. Security Overview</p>
-
-                <p>16.1 All Merchant and Customer data is written to multiple disks instantly, backed up daily, and stored in multiple locations. Files that Customers upload are stored on servers that use modern techniques to remove bottlenecks and points of failure.</p>
-
-                <p>16.2 Whenever data is in transit between Merchant and The Subline, everything is encrypted, and sent using HTTPS.</p>
-
-                <p>16.3 All credit card transactions are processed using secure encryption are handled via Third Party Payment Providers.</p>
-            </div>
-        </div>
-
-        <div id="helpStrand" class="strand">
-            <h1>Found a bug?  Need help?  Have a suggestion? Need to resest your Password?</h1>
-
-            <h1>Send us an email at <a href="mailto:lee@thesubline.com">lee@thesubline.com</a></h1>
-        </div>
-        
-        <script src="/informationPage/help.js"></script>
-        <script src="/informationPage/legal.js"></script>
-    </body>
-</html>

+ 0 - 13
views/informationPage/legal.js

@@ -1,13 +0,0 @@
-window.legalObj = {
-    display: function(){
-        document.getElementById("legalStrand").style.display = "flex";
-        document.getElementById("helpStrand").style.display = "none";
-
-        document.getElementById("joinButton").style.display = "none";
-        let button = document.getElementById("logInButton");
-        button.innerText="HELP";
-        button.onclick = ()=>{helpObj.display()};
-    }
-}
-
-legalObj.display();

+ 181 - 0
views/informationPages/help.ejs

@@ -0,0 +1,181 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta content="width=device-width, initial-scale=1" name="viewport"/>
+        <title>The Subline</title>
+        <link rel="icon" type="img/png" href="/shared/images/logo.png">
+        <link rel="stylesheet" href="/shared/shared.css">
+        <link rel="stylesheet" href="/informationPages/information.css">
+        <link href="https://fonts.googleapis.com/css?family=Saira&display=swap" rel="stylesheet">
+    </head>
+    <body>
+        <% include ../shared/header %>
+
+        <div class="helpMenu">
+            <ul>
+                <li><a href="#menu">Menu</a></li>
+                <li><a href="#dashboard">Dashboard</a></li>
+                <li><a href="#ingredients">Ingredients</a>
+                    <ul class="indent">
+                        <li><a href="#ingredientsNew">New Ingredients</a>
+                            <ul class="indent">
+                                <li><a href="#ingredientsNewBottles">Bottle Unit</a></li>
+                            </ul>
+                        </li>
+                        <li><a href="#ingredientsDetails">Ingredient Details</a></li>
+                    </ul>
+                </li>
+                <li><a href="#recipeBook">Recipe Book</a>
+                    <ul class="indent">
+                        <li><a href="#recipeBookDetails">Recipe Details</a></li>
+                        <li><a href="#recipeBookNew">New Recipes</a></li>
+                    </ul>
+                </li>
+                <li><a href="#analytics">Analytics</a>
+                    <ul class="indent">
+                        <li><a href="#analyticsIngredients">IngredientAnalytics</a></li>
+                        <li><a href="#analyticsRecipes">Recipe Analytics</a></li>
+                    </ul>
+                </li>
+                <li><a href="#orders">Orders</a>
+                    <ul class="indent">
+                        <li><a href="#ordersDetails">Order Details</a></li>
+                        <li><a href="#ordersNew">New Orders</a></li>
+                    </ul>
+                </li>
+                <li><a href="#transactions">Transactions</a>
+                    <ul class="indent">
+                        <li><a href="#transactionsDetails">Transaction Details</a></li>
+                        <li><a href="#transactionsNew">New Transactions</a></li>
+                    </ul>
+                </li>
+                <li><a href="#contact">Contact Us</a></li>
+            </ul>
+        </div>
+
+        <div class="helpContent">
+            <h2 id="menu">MENU</h2>
+            
+            <p>After logging in, you will see the menu on the left side of the screen.  This menu will help you to navigate the website and view all of your content.If at any time you feel that the menu is taking up too much screen space, then simply click on the three horizontal bars in the top-right of the menu and it will shrink down to take up less space.</p>
+
+            <p>There are seven different options for you to choose on the menu. "DASHBOARD", "INGREDIENTS", "RECIPE BOOK", "ANALYTICS", "ORDERS", "TRANSACTIONS", and "LOGOUT".  The first six options will take you to the different pages on the website, which are discussed below.  The final option, "LOGOUT", will log you out of the website and take you back to the home page.</p>
+
+            <h2 id="dashboard">DASHBOARD</h2>
+
+            <p>The dashboard page is the main page that you will see after you have logged in to your account.  There are a number of elements on the page that are designed to get you some of the most important information right off the bat</p>
+
+            <p>In the top left of the page, you will will see total revenue for the month.  This revenue is calculated from the sales of recipes from the first day of the month to the current day.  Below the revenue you can see a comparison to last month.  This shows whether your revenue is more or less than the same period from last month and by what percent.  This compares the same time period.  So if today is September 15th, it will compare be comparing September 1 - September 15 to October 1 - October 15</p>
+
+            <p>To the right of the revenue, you can see a graph of the revenue.  This graph displays the revenue per day for the last 30 days so that you can get an idea of which days you are selling more or less.</p>
+
+            <p>In the bottom left is the "Inventory Check".  These are some ingredients that are recommend to be checked to ensure that your actual stock matches the estimated stock.  If you need to update any of them, then you can simply enter in the new number and then click "UPDATE" and the ingredients will automatically be updated</p>
+
+            <p>The final part of the dashboard page is the most popular ingredients in the bottom right.  This shows a simple chart of the five most popular ingredients that you have on in your restaurant along with the amount that you have sold.</p>
+
+            <h2 id="ingredients">INGREDIENTS</h2>
+            <p>The ingredients page is where you will find all of the information about each of your individual ingredients.  By default, your ingredients are sorted according to their category.  If you click on the arrow to the right of the category, it will expand and you can see all of the ingredients within that category and your stock of each one.</p>
+
+            <p>Near the top of the page, on the left, is a filter option.  Here you can search for specific ingredients.  Start typing in the ingredient or ingredients that you want to search for and an uncategorized list of ingredients matching your search will appear.  click the red "X" to clear the filter.</p>
+
+            <p>Near the top of the page and on the right is a "sort by" option.  If you click on this then a dropdown will appear with several options.  The "Ingredient" option will display an uncategorized list of all ingredients in alphabetical order.  The "Category" option will show a categorized list, which is the default display.  The "Unit of Measurement" option will categorize your ingredients based on their measurement units, similar to "Category".  Click on the red "X" at any time to revert to the default.</p>
+
+            <h3 id="ingredientsNew">NEW INGREDIENTS</h3>
+            <p>There is a button in the top right of the ingredients page labeled "NEW".  Clicking on this will open a sidebar on the right that allows you to create a new ingredient.  There are 4 fields that you need to fill out in order to create a new ingredient.  The name is whatever you want to call the ingredient and the category can be used to group it together with other similar ingredients.  The quantity is the number amount that you have in stock of that particular ingredient.  Match this with a unit in the next field.  When choosing a unit, keep in mind that you will be able to freely change the unit in the future.  However, you can only change units with the same type.  For example, you can change LB (mass), to KG (mass), but you cannot change LB (mass) to GAL(volume).</p>
+
+            <h4 id="ingredientsNewBottles">BOTTLE UNIT</h4>
+            <p>One of the special type of ingredients that can choose is the bottle type.  Bottles, obviously, are for things that purchase as bottles (i.e. liquour, wine) but tend to sell as smaller units(i.e. shots, glasses).  If you choose the bottle option, then another field will appear asking for bottle size.  Here you need to choose the unit (volume) that the bottle is measured in and then the quantity of each bottle.  The unit for the bottle can be changed at any time.  When creating recipes for bottle items, which is discussed further down, you will add in the volume that the recipe has and it will update the amount of the ingredient in bottles automatically</p>
+
+            <h3 id="ingredientsDetails">INGREDIENT DETAILS</h3>
+            <p>You can click on any of the ingredients to get more details on it.  After clicking on an ingredient, a sidebar should appear to the right.  At the top of this is the category and name of the ingredient.  Next is the current stock that you should have for this item.  After that is the average daily use for the past 30 days.  As the name suggests, this show how much you use this ingredient every day, on average.  Last is the recipes.  This simply shows all of your recipes that contain this ingredient.  You can click on the recipes to get further information on that recipe.  We discuss recipes further down.</p>
+
+            <p>At the top of the sidebar are three icons.  The arrow on the left will close the sidebar.  The trash can on the right will delete the ingredient.  In order to delete an ingredient, it must first be removed from all recipes that contain the ingredient.  Be careful, this cannot be undone.  The pencil icon in the center will allow you to edit the ingredient.  You can update the name, category and current stock of the current ingredient.  Also, you can change the units that want to use.  So, if you use pounds for an ingredient, but decide to switch over to metric, then your can change it to kilograms.  Or the other way around.  Click save to make the changes, or cancel to cancel them.</p>
+
+            <h2 id="recipeBook">RECIPE BOOK</h2>
+            <p>The third option on the menu is the "RECIPE BOOK".  Here you will find all of the information that you need about all of your recipes.  The recipes are displayed individual with the price of that recipe.</p>
+
+            <p>Above the recipes and on the left is a filter option.  This works similarly to the ingredients filter.  If you start typing in this box, then the list will be filtered to show only the ingredients that match what you have typed in.</p>
+
+            <h3 id="recipeBookDetails">RECIPE DETAILS</h3>
+            <p>Clicking on any of your recipes will open a sidebar on the right that will display all of the details for that specific ingredient.  At the top of the sidebar is the name of the ingredient.  Below that is a list of the ingredients contained within the recipe.  Below that is the price of the recipe.</p>
+
+            <p>Above the name of the recipe are three icons.  The arrow icon on the left will close the sidebar.  The trash can icon on the right will delete the recipe.  Be careful, deleting a recipe cannot be undone.</p>
+
+            <p>The pencil icon in the center is for editing the recipe.  If you click on this, you will be able to change the name of the recipe, add/remove/edit ingredients in the recipe, and change the price.  To edit the quantity of an ingredient within that recipe, simply change the number in the box to the right of the ingredient. To add an ingredient to the recipe, click on the "+" icon.  A dropdown will appear with ingredients for you to choose from.  Choose the ingredient you want to add, then the enter the quantity of the ingredient within the recipe in the box to the right.  To remove an ingredient from the recipe, click on the trash can to the right of the ingredient.  This will only remove the ingredient from the recipe, not from your ingredient list.  When finished, click the update button to save all changes</p>
+
+            <h3 id="recipeBookNew">New Recipes</h3>
+            <p>Click on the "NEW" button on the top left of the recipe book page will open a sidebar on the right for you to create a new recipe.  The box at the top contains three fields: name, price and # of ingredients.  First enter the name and the price that you sell the recipe for.  In "# of ingredients" enter the number of ingredients that you have in the recipe.  You can update this number any time.  After that, you should see an appropriate number of boxes below "INGREDIENTS".  In each box, choose the ingredient and the quantity of that ingredient that is used in that recipe.  When finished, click on "CREATE" at the bottom to save the new recipe.</p>
+
+            <h2 id="analytics">ANALYTICS</h2>
+            <p>The analytics page is the place where you can find detailed information about both your ingredients and your recipes.  There are many data points for you to explore on this page.  By default you will see ingredients first.</p>
+
+            <p>At the top right of the page you can see the option to select two dates.  This allows you to get data between any two time periods. Enter the first day that you would like to see in "From" and the last day in "To".  Then click on submit once you are done to retrieve that data.  Depending on the amount of data, this may take up to minute.  Updating the dates will apply to both ingredients and recipes.  By default, the page displays the last 30 days of data until you choose different dates.</p>
+
+            <h3 id="analyticsIngredients">INGREDIENT ANALYTICS</h3>
+            <p>The ingredients part of analytics is displayed when first visiting the analytics page.  This is indicated by the selector at the top-center of the page.  At first you will see not data until you choose an ingredient.  All of your ingredients are listed on the left side of the page.  Choose an ingredient to see the data for that specific ingredient.</p>
+
+            <p>The largest box at the top of the page shows a graph for the selected ingredient.  It shows the quantity sold for the ingredient day by day.  It is an interactive graph and you can zoom in, zoom out and many other things to better visualize your data.</p>
+
+            <p>The row below the graph has three different metrics.  "Minimum Daily Use" displays the minimum amount of that ingredient that you used during the selected time period.  "Average Daily Use" displays the average amount that is used each day for the selected time period.  "Maximum Daily Use" will display the maximum amount that you used in a single day.</p>
+
+            <p>The bottom row displays the average quantity of the ingredient that was used for each day of the week during the selected time period.</p>
+
+            <h3 id="analyticsRecipes">RECIPE ANALYTICS</h3>
+            <p>To switch to recipe analytics, click on the "Recipes" part of the selector at the top of the page.  By default, no recipe will be displayed at first.  On the left side of the page you will see a list of all of your recipes.  Once you choose one of your recipes, you will be shown the data for that recipe.</p>
+
+            <p>The main part of the page is the graph.  This displays all the quantity of that recipe sold day by day for your selected time period.  As with the ingredient graph, it is interactive and you can change it to better display your data.</p>
+
+            <p>At the bottom of the page are two data points.  On the left is average daily sales.  This display the average times that this recipe was sold each day for the selected time period.  On the right is the average daily revenue.  This is the average revenue, per day, for that specific recipe.</p>
+
+            <h2 id="orders">ORDERS</h2>
+            <p>The orders page displays all of the information about your orders.  You can search and view information about every time that you have purchased ingredients, as well as create new orders when you purchase more ingredients.</p>
+
+            <p>In the center of the page you will see a list of all of your orders.  It displays the name or ID of the order, the number of unique ingredients purchased, the date, and the total cost of the order.  By default, your 100 most recent orders are displayed.</p>
+
+            <p>Above the list of your orders is a search option.  Clicking on "Dates" will open a dropdown that allows you to select two dates to search between.  Clicking on "Ingredients" will open a dropdown with a list of all of your ingredients.  Check the box next to each ingredient to search for any orders that contain that ingredient.  Leaving all boxes unchecked will get all orders between the dates that you selected.  Finally, click on "Submit" to retrieve the orders.  Depending on the number of orders that must be retrieved, this could take several moments.</p>
+
+            <h3 id="ordersDetails">ORDER DETAILS</h3>
+            <p>Clicking on any specific order on the orders page will open a sidebar on the right with detailed information about the order.  At the top is the order name/ID, followed by the date.  Below this will be a list of all ingredients purchased in this order.  The ingredients list will show their name, the quantity purchased, the unit price and the total price for that ingredient.  At the bottom is displayed the total price of all ingredients within that order.</p>
+
+            <p>At the top of the sidebar are two icons.  The arrow icon on the left will close the sidebar.  The trash can icon on the right will delete the order.  Be careful, deleting an order cannot be undone.  It is not possible to edit orders.  If you find that you have made a mistake when entering an order, then you will have to delete it and create a new one.</p>
+
+            <h3 id="ordersNew">NEW ORDERS</h3>
+            <p>At the top-right of the orders page is the "NEW" button to create a new order.  Clicking on this will open a sidebar on the right.  On the left side of this sidebar is a list of all of your ingredients.  On the right is the data for the order.</p>
+
+            <p>To create a new order, start by entering a name/ID for the order.  This can be anything you want, it is only for your reference and convenience.  Then enter the date that the order was created.</p>
+
+            <p>To add ingredients to the order, go through your list of ingredients and click on any that were purchased with this order.  Once you click on the ingredient it will be added to the order on the right.  Each ingredient has two boxes.  The box on the left is the quantity of the ingredient that was purchased in this order.  On the left is the price per unit of the ingredient.  Be sure to enter the price per unit (i.e. $/LB or $/GAL) and not the total price for the ingredient.  If you have accidently added an ingredient that is not part of this order, then click on the "remove" button at the top-right of the ingredient.  This will remove the ingredient from the order.</p>
+
+            <p>Once you have finished adding all of the ingredients you purchased to this order, click on the "CREATE" button at the bottom of the sidebar.  This will create the new order with all of the specified ingredients.  When a new order is created, the stock for all of your ingredients will be automatically updated.  If you go back to the recipe page then you can see that all of the ingredients in the order have been updated accordingly.</p>
+
+            <h2 id="transactions">TRANSACTIONS</h2>
+            <p>The transactions page is the place where you can find all of the sales that you have made.  In the center is a list of the 100 most recent transactions.  Each transaction displays the date and/or time of the transaction, the total number of recipes sold in the transaction, and the revenue from the transaction.</p>
+
+            <p>Above the transaction list is a search bar similar to the orders page.  Clicking on "Dates" will open a dropdown with two date fields.  Enter a start and end date to find your orders.  Clicking on "Recipes" will open a dropdown with all of your recipes.  Click on the checkbox next to each recipe that you want to search for.  Choosing a recipe will search for any transactions that contain that recipe.  If you don't check any recipes, your will recieve a list of all transactions between the previously specified dates.  Click on submit to recieve the transactions that you chose.  Depending on the number of transactions, this could take several moments.</p>
+
+            <h3 id="transactionsDetails">TRANSACTION DETAILS</h3>
+            <p>On the transactions page, clicking on any transaction will open a sidebar display detailed information for that particular transaction.  At the top, you will see the date and maybe the time of the transaction.  Below this will be a list of recipes that were sold in this transaction.  Each recipe in this list will show the name of the recipe, the number sold, the cost of the recipe and the total revenue from that specific recipe within the transaction.  Below this will be your totals for the transaction.  It will show the total number of recipes sold and the total revenue for the transaction.</p>
+
+            <p>At the top of the sidebar are two icons.  The arrow icon on the left will close the sidebar.  The trash can icon on the right will delete the transaction.  Be careful, deleting a transaction cannot be undone.  Editing transactions is not available.  If you find that you have made a mistake when creating a transaction, you will have to delete it and then recreate that transaction.</p>
+
+            <h3 id="transactionsNew">NEW TRANSACTIONS</h3>
+            <p>At the top-right of the transactions page is the "NEW" button for creating new transactions.  Clicking on this button will open a sidebar on the right.</p>
+
+            <p>Creating a transaction is pretty simple.  First, choose the date of the transaction at the top.  Below that you will a list of all of your recipes.  For each recipe enter the number sold in the box to the right of the recipe name.  for recipes that were not sold in the transaction, simply leave the field blank.  Since it is impractical to enter every single transaction from your business, we recommend creating one transaction per day for all recipes sold.</p>
+
+            <p>When finished entering in all information, click the "CREATE" button at the bottom of the sidebar.  This will add your new transaction.  Also, all of your ingredients will be automatically updated based on the quantity of recipes sold, and the quantity of each ingredient within the recipe.  You can return to the ingredients page after creating a new transaction to find that the quantities of your ingredients have already been updated.</p>
+
+            <h2 id="contact">CONTACT US</h2>
+            <p>If you have found any problems with the site, or need help that is not covered here, please reach out to us.</p>
+
+            <p>This site is constantly being updated to provide a better experience to our users and better meet their needs.</p>
+
+            <a href="mailto:lee@thesubline.com">lee@thesubline.com</a>
+        </div>
+
+        <script>
+            document.getElementById("privacyPolicyLink").classList.remove("headerHide");
+            document.getElementById("termsLink").classList.remove("headerHide");
+        </script>
+    </body>
+</html>

+ 59 - 0
views/informationPages/information.css

@@ -0,0 +1,59 @@
+body{
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+ol{
+    list-style: decimal inside none;
+}
+
+    ol > *{
+        margin: 10px;
+    }
+
+.content{
+    padding: 50px;
+    width: 75%;
+}
+    .content > *{
+        margin: 15px;
+    }
+
+/*
+HELP PAGE
+*/
+.helpMenu{
+    position: fixed;
+    top: 75px;
+    left: 0;
+    padding: 100px 50px;
+}
+
+    .helpMenu ul{
+        list-style-type: none;
+    }
+
+    .indent{
+        margin-left: 20px;
+    }
+
+.helpContent{
+    text-align: center;
+    width: 50%;
+    padding: 50px;
+}
+
+    .helpContent h2, .helpContent h3, .helpContent h4{
+        margin: 20px 0;
+    }
+
+    .helpContent h2{
+        font-size: 25px;
+    }
+
+    .helpContent p{
+        margin: 15px 0;
+        text-align: left;
+        font-size: 20px;
+    }

+ 208 - 0
views/informationPages/privacyPolicy.ejs

@@ -0,0 +1,208 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta content="width=device-width, initial-scale=1" name="viewport"/>
+        <title>The Subline</title>
+        <link rel="icon" type="img/png" href="/shared/images/logo.png">
+        <link rel="stylesheet" href="/shared/shared.css">
+        <link rel="stylesheet" href="/informationPages/information.css">
+        <link href="https://fonts.googleapis.com/css?family=Saira&display=swap" rel="stylesheet">
+    </head>
+    <body>
+        <% include ../shared/header %>
+
+        <div class="content">
+            <h1>The Subline, LLC Privacy Policy</h1>
+        
+            <p>Last modified: December 23, 2019</p>
+        
+            <h3>Introduction</h3>
+        
+            <p>The Subline, LLC. ("Company" or "We") respects your privacy, and we are committed to protecting it through our compliance with this policy.</p>
+        
+            <p>This policy describes the types of information we may collect from you or that you may provide when you visit the website, www.thesubline.com (the "Website") or utilize any of the services on our platform, including our practices for collecting, using, maintaining, protecting, and disclosing that information.</p>
+        
+            <p>This policy applies to information we collect:</p>
+        
+            <ol>
+                <li>On our Website and our Platform;</li>
+        
+                <li>In emails, texts, and other electronic messages between you and the Website and our Platform in connection with inventory management (procurement, sales, management, etc.);</li>
+        
+                <li>When you interact with our advertising and applications on third-party websites and services, if those applications or advertising include links to this policy.</li>
+            </ol>
+        
+            <p>Please read this policy carefully to understand our policies and practices regarding your information and how we will treat it. If you do not agree with our policies and practices, your choice is not to use our Website or Platform. By accessing or using our Website and Platform, you agree to this privacy policy. This policy may change from time to time (see Changes to Our Privacy Policy). Your continued use of our Website and the Platform after we make changes is deemed to be acceptance of those changes, so please check the policy periodically for updates.</p>
+        
+            <h3>Information We Collect About You and How We Collect It</h3>
+        
+            <p>Generally, no personally identifiable information, such as your name or mailing address, is collected anytime you visit the Website/Platform, nor is it required for you to visit the Website and our Platform. We collect several types of information from and about users of our Website and our Platform, including information:</p>
+        
+            <ol>
+                <li>by which you may be personally identified, such as name, postal address, e-mail address, credit card information, or telephone number ("personal information");</li>
+        
+                <li>about your internet connection, the equipment you use to access our Website and our Platform and usage details.</li>
+            </ol>
+        
+            <h3>We collect this information:</h3>
+        
+            <ol>
+                <li>Directly from you when you provide it to us;</li>
+        
+                <li>Automatically as you navigate through the site. Information collected automatically may include usage details, IP addresses, and information collected through cookies;</li>
+        
+                <li>From third parties, for example, our business partners.</li>
+            </ol>
+        
+            <h3>Information You Provide to Us</h3>
+        
+            <p>When personal information is collected from you, such as your name and email address, we generally let you know at the time of collection how we will use the personal information. The Company will not share or sell the personal information you provide to us with anyone for their marketing use. We may share the personal information you provide with other companies we have hired to provide services for us. Your information will only be used by these third parties (or their subcontractors) to perform the assigned function.</p>
+        
+            <p>The information we collect on or through our Website and our Platform may include:</p>
+        
+            <ol>
+                <li>Information that you provide by filling in forms on our Website and our Platform. This includes information provided at the time of registering or requesting further services. We may also ask you for information when you enter a contest or promotion sponsored by us, and when you report a problem with our Website or Platform.</li>
+        
+                <li>Records and copies of your correspondence (including email addresses), if you contact us.</li>
+        
+                <li>Your responses to surveys that we might ask you to complete for research purposes.</li>
+            </ol>
+        
+            <p>You also may provide information to be published or displayed (hereinafter, "posted") on public areas of the Website and our Platform, or transmitted to other users of the Website and our Platform or third parties (collectively, "User Contributions"). Your User Contributions are posted on and transmitted to others at your own risk. We cannot control the actions of other users of the Website and our Platform with whom you may choose to share your User Contributions. Therefore, we cannot and do not guarantee that your User Contributions will not be viewed by unauthorized persons.</p>
+        
+            <h3>Information We Collect Through Automatic Data Collection Technologies</h3>
+        
+            <p>As you navigate through and interact with our Website and our Platform, we may use automatic data collection technologies to collect certain information about your equipment, browsing actions, and patterns, including:</p>
+        
+            <ol>
+                <li>Details of your visits to our Website/Platform, including traffic data, location data, logs, and other communication data and the resources that you access and use on the Website/Platform;</li>
+        
+                <li>Information about your computer and internet connection, including your IP address, operating system, and browser type.</li>
+            </ol>
+        
+            <p>The information we collect automatically is statistical data and does not include personal information, but we may maintain it or associate it with personal information we collect in other ways or receive from third parties. It helps us to improve our Website and our Platform and to deliver a better and more personalized service, including by enabling us to:</p>
+        
+            <ol>
+                <li>Estimate our audience size and usage patterns.</li>
+        
+                <li>Store information about your preferences, allowing us to customize our Website and our Platform according to your individual interests.</li>
+        
+                <li>Speed up your searches.</li>
+        
+                <li>Recognize you when you return to our Website and our Platform.</li>
+            </ol>
+        
+            <p>The technologies we use for this automatic data collection may include:</p>
+        
+            <ol>
+                <li>Cookies (or browser cookies). A cookie is a small file placed on the hard drive of your computer. You may refuse to accept browser cookies by activating the appropriate setting on your browser. However, if you select this setting you may be unable to access certain parts of our Website. Unless you have adjusted your browser setting so that it will refuse cookies, our system may issue cookies when you direct your browser to our Website and our Platform.</li>
+        
+                <li>Flash Cookies. Certain features of our Website and our Platform may use local stored objects (or Flash cookies) to collect and store information about your preferences and navigation to, from, and on our Website and our Platform. Flash cookies are not managed by the same browser settings as are used for browser cookies. For information about managing your privacy and security settings for Flash cookies, see Choices About How We Use and Disclose Your Information.</li>
+        
+                <li>Web Beacons. Pages of our Website and our Platform and our e-mails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or and for other related website statistics (for example, recording the popularity of certain website content and verifying system and server integrity).</li>
+            </ol>
+        
+            <p>We do not collect personal information automatically, but we may tie this information to personal information about you that we collect from other sources or you provide to us.</p>
+        
+            <h3>How We Use Your Information</h3>
+        
+            <p>We use information that we collect about you or that you provide to us, including any personal information:</p>
+        
+            <ol>
+                <li>To present our Website and our Platform and its contents to you;</li>
+        
+                <li>To provide you with information, products, or services that you request from us;</li>
+        
+                <li>To fulfill any other purpose for which you provide it;</li>
+        
+                <li>To carry out our obligations and enforce our rights arising from any contracts entered into between you and us, including for billing and collection;</li>
+        
+                <li>To notify you about changes to our Website and our Platform or any products or services we offer or provide though it;</li>
+        
+                <li>To allow you to participate in interactive features on our Website and our Platform or participate in promotions, reward programs or other similar programs;</li>
+        
+                <li>In any other way we may describe when you provide the information;</li>
+        
+                <li>For any other purpose with your consent.</li>
+            </ol>
+        
+            <h3>Disclosure of Your Information</h3>
+        
+            <p>We may disclose aggregated information about our users, and information that does not identify any individual, without restriction. We may disclose personal information that we collect or you provide as described in this privacy policy:</p>
+        
+            <ol>
+                <li>To our subsidiaries and affiliates.</li>
+        
+                <li>To contractors, service providers, and other third parties we use to support our business and who are bound by contractual obligations to keep personal information confidential and use it only for the purposes for which we disclose it to them.</li>
+        
+                <li>To a buyer or other successor in the event of a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Company's assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which personal information held by Company about our Website users is among the assets transferred.</li>
+        
+                <li>For any other purpose disclosed by us when you provide the information.</li>
+        
+                <li>With your consent.</li>
+            </ol>
+        
+            <p>We may also disclose your personal information:</p>
+        
+            <ol>
+                <li>To comply with any court order, law, or legal process, including to respond to any government or regulatory request.</li>
+        
+                <li>To enforce or apply our terms of use or terms of sale and other agreements, including for billing and collection purposes.</li>
+        
+                <li>If we believe disclosure is necessary or appropriate to protect the rights, property, or safety of Company, our customers, or others. This includes exchanging information with other companies and organizations for the purposes of fraud protection and credit risk reduction.</li>
+            </ol>
+        
+            <h3>Choices About How We Use and Disclose Your Information</h3>
+        
+            <p>We strive to provide you with choices regarding the personal information you provide to us. We have created mechanisms to provide you with the following control over your information:</p>
+        
+            <ol>
+                <li>Tracking Technologies and Advertising. You can set your browser to refuse all or some browser cookies, or to alert you when cookies are being sent. To learn how you can manage your Flash cookie settings, visit the Flash player settings page on Adobe's website. If you disable or refuse cookies, please note that some parts of this site may then be inaccessible or not function properly.</li>
+            </ol>
+        
+            <h3>Accessing and Correcting Your Information</h3>
+        
+            <p>You may also send us an email at the address shown below to request access to, correct or delete any personal information that you have provided to us. We cannot delete your personal information except by also deleting your user account. We may not accommodate a request to change information if we believe the change would violate any law or legal requirement or cause the information to be incorrect.</p>
+        
+            <h3>Your California Privacy Rights</h3>
+        
+            <p>California Civil Code Section § 1798.83 permits users of our Website and our Platform that are California residents to request certain information regarding our disclosure of personal information to third parties for their direct marketing purposes. To make such a request, please send an email to info@thesubline.com or write us at the address noted below.</p>
+        
+            <h3>Data Security</h3>
+        
+            <p>We have implemented measures designed to secure your personal information from accidental loss and from unauthorized access, use, alteration, and disclosure. All information you provide to us is stored on our secure servers behind firewalls. Any payment transactions will be encrypted using SSL technology.</p>
+        
+            <p>The safety and security of your information also depends on you. Where we have given you (or where you have chosen) a password for access to certain parts of our Website and our Platform, you are responsible for keeping this password confidential. We ask you not to share your password with anyone.</p>
+        
+            <p>Unfortunately, the transmission of information via the internet is not completely secure. Although we do our best to protect your personal information, we cannot guarantee the security of your personal information transmitted to our Website and our Platform. Any transmission of personal information is at your own risk. We are not responsible for circumvention of any privacy settings or security measures contained on the Website and our Platform.</p>
+        
+            <h3>Changes to Our Privacy Policy</h3>
+        
+            <p>It is our policy to post any changes we make to our privacy policy on this page. If we make material changes to how we treat our users' personal information, we will notify you through a notice on the Website home page. The date the privacy policy was last revised is identified at the top of the page. You are responsible for ensuring we have an up-to-date active and deliverable email address for you, and for periodically visiting our Website and our Platform and this privacy policy to check for any changes.</p>
+        
+            <h3>Miscellaneous</h3>
+        
+            <p>Disclaimer Notice. We make no claims, promises or guarantees about the accuracy, currency, completeness or adequacy of the content of the Website and our Platform, products and services available through the Website and our Platform, and expressly disclaim liability for errors and omissions in the related content. No warranty of any kind, implied, express or statutory, including but not limited to warranties of non-infringement of third-party rights, title, merchantability, fitness for a particular purpose and freedom from computer virus is given with respect to the Website and our Platform, products and services offered through the Website and our Platform. Although we have put in place security systems (as described in this Privacy Policy) that are designed to prevent unauthorized disclosure of your Personal Information, due to the nature of internet technologies we cannot provide assurances as to the security of this information, and expressly disclaim any such obligation.</p>
+        
+            <p>Choice of Law and Jurisdiction. This Privacy Policy shall be construed in accordance with the laws of the State of Florida, without regard to any conflict of law provisions. Any dispute arising under this Privacy Policy shall be resolved exclusively by the state or federal courts sitting in the judicial district that includes Seattle, Washington.</p>
+        
+            <h3>Contact Information</h3>
+        
+            <p>To ask questions or comment about this privacy policy and our privacy practices, contact us at:</p>
+        
+            <p class="contactInfo">The Subline</p>
+        
+            <p class="contactInfo">ATTN: PRIVACY POLICY</p>
+        
+            <p class="contactInfo">info@thesubline.com</p>
+        
+            <p class="contactInfo last">www.thesubline.com</p>
+        </div>
+
+        <script>
+            document.getElementById("termsLink").classList.remove("headerHide");
+            document.getElementById("helpLink").classList.remove("headerHide");
+        </script>
+    </body>
+</html>

+ 302 - 0
views/informationPages/terms.ejs

@@ -0,0 +1,302 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta content="width=device-width, initial-scale=1" name="viewport"/>
+        <title>The Subline</title>
+        <link rel="icon" type="img/png" href="/shared/images/logo.png">
+        <link rel="stylesheet" href="/shared/shared.css">
+        <link rel="stylesheet" href="/informationPages/information.css">
+        <link href="https://fonts.googleapis.com/css?family=Saira&display=swap" rel="stylesheet">
+    </head>
+    <body>
+        <% include ../shared/header %>
+
+        <div class="content">
+            <h1>The Subline, Terms and Conditions</h1>
+
+            <h4>Last update: January 8, 2020</h4>
+
+            <p>These terms and conditions specify the terms of the contract between The Subline and the Merchant Venue named in the Sign-Up Form (&quot;Merchant Venue&quot;), pursuant to which the Merchant Venue may use the The Subline service. If the Merchant Venue does not accept these terms and conditions (including following any amendment to these terms and conditions), the Merchant Venue must immediately cease use of the Service and notify The Subline in writing.</p>
+
+            <p>By using the Service, the Merchant Venue agrees to be legally bound by the Agreement (as varied from time to time), and to comply with all The Subline policies, including the The Subline Privacy Policy (both of which are located at hazInut.com/privacy-policy/,</p>
+
+            <p>1. General</p>
+
+            <p>1.1 The Service provides an inventory management solution for food and beverage providers using Clover devices. Information and analysis provided by The Service is available on a personalized and private desktop browser-based dashboard.</p>
+
+            <p>1.2 Much of the analysis completed by The Service is based on Merchant Information stored on Clover servers. After successful login (granted by Clover) to the Merchant Dashboard, The Service will pull information from the Clover servers, update the information, and display it to the Merchant.</p>
+
+            <p>1.3 This Agreement specifies the terms on which The Subline makes available, and Customer will use, the Service.</p>
+
+            <p>2. Definitions and Interpretation</p>
+
+            <p>2.1 In this Agreement:</p>
+
+            <p>&quot;Agreement&quot; means this Merchant Venue Agreement (as amended or updated from time to time), including the Subscription Plan and these terms and conditions.</p>
+
+            <p>&quot;The Subline Service Fee&quot; means the monthly Fee payable by the Merchant Venue to The Subline for the Service as detailed in the Subscription Plan.</p>
+
+            <p>&quot;HazInut Service Fee Start Date&quot; means the first date from which the The Subline Service Fee is charged to the Merchant Venue, as detailed in the Subscription Plan.</p>
+
+            <p>&quot;Chargeback&quot; means a demand by a bank or other credit-card provider, or other provider of electronic payment facilities, for a merchant to make good the loss on a fraudulent or disputed transaction.</p>
+
+            <p>&quot;Customer&quot; means anyone who communicates an Order, in person or using third-party applications to the Merchant Venue for the supply of Venue Products.</p>
+
+            <p>&quot;Menu&quot; means a listing of Venue Products that the Merchant Venue will sell to Customers (including the price a Customer must pay to purchase each item of Venue Products) that The Subline will publish to, and process Orders from, Customers by means of the Service Portals.</p>
+
+            <p>&quot;Order&quot; means an offer from a Customer to Merchant Venue to purchase Venue Products for the Order Price.</p>
+
+            <p>&quot;Order Price&quot; means the price a Customer will pay to Merchant Venue for the supply of Venue Products (exclusive of GST and any other applicable taxes), which must be calculated in accordance with pricing specified on the Merchant Venue's Menu.</p>
+
+            <p>&quot;Merchant Venue&quot; means an establishment that utilizes The Subline for Inventory Management.</p>
+
+            <p>&quot;Merchant Venue Material&quot; includes any material in which Merchant Venue owns or holds intellectual property rights, or which commercially identifies, or can be used to commercially identify, the Merchant Venue, including its name, logos, trademarks, slogans, Menu, names of Venue Products, pricing and location.</p>
+
+            <p>&quot;Point of Supply&quot; means the time and location at which Merchant Venue supplies Venue Products to a Customer, which may include the Merchant Venue's business premises (for Venue Products collected by the Customer).</p>
+
+            <p>&ldquo;Merchant Purchase&rdquo; means any purchase a raw material (food, beverage, condiments, utensils, plates, etc) that the Merchant sells to the Customer as a routine part of the Merchant&rsquo;s business.</p>
+
+            <p>&quot;Venue Products&quot; means any product that Merchant Venue supplies, or makes available for supply, to any Customer, including any type of food or beverage.</p>
+
+            <p>&quot;Service&quot; means the provision of online facilities to enable Merchants to view real-time inventory status, Orders, Merchant Purchases and the analytics provided.</p>
+
+            <p>&quot;Service Portals&quot; means facilities by which The Subline makes the Service available to Merchants from time to time.</p>
+
+            <p>&quot;Sign Up Form&quot; means a document with the name, address and email of the Merchant Venue that is completed, usually at the time of the Merchant Venue agrees to use The Subline.</p>
+
+            <p>&quot;Subscription Plan' means the terms of purchase agreed by Merchant Venue via the channel used to acquire The Subline including but not limited to The Subline recurring Fee, currency of the The Subline Service Fee and The Subline Service Fee start date.</p>
+
+            <p>&quot;Other Conditions&quot; means any additional terms and conditions agreed to by a Merchant venue for services other than the standard ones, and via additional side agreements,</p>
+
+            <p>2.2 In this Agreement:</p>
+
+            <p>a. headings to and within clauses are for convenience and reference only and do not form a part of this Agreement and shall not in any way affect the interpretation of this Agreement;</p>
+
+            <p>b. Words importing the singular include the plural and vice versa; and</p>
+
+            <p>c. All monetary references are the currency detailed in the Subscription Plan otherwise specified.</p>
+
+            <p>2.3 This Agreement incorporates any Other Conditions, and in the event of any inconsistency between any of these terms and conditions and any Other Conditions, the Other Conditions will prevail to the extent of the inconsistency.</p>
+
+            <p>3. Service and availability</p>
+
+            <p>3.1 The Subline is the owner and operator of the Service Portals. The Service Portals allows Merchants to monitor their inventory and receive information from The Service.</p>
+
+            <p>3.2 The Subline reserves the right to provide the Services on other Service Portals in the future, without notice to the Merchant Venue.</p>
+
+            <p>3.3 The Merchant Venue operates one or more Venue(s), at which the Venue Products are prepared and sold to the public.</p>
+
+            <p>3.4 The Subline will:</p>
+
+            <p>a. make the Service available for use by the public through the Service Portals;</p>
+
+            <p>b. provide current Inventory counts based on information provided by the Merchant;</p>
+
+            <p>3.5 The Merchant Venue acknowledges that The Subline has no responsibility for the fulfilment of Orders of Raw Materials or Ingredients; The Subline does not hold any Collected Payments.</p>
+
+            <p>3.6 The Subline may without notice make changes to or temporarily suspend the operation of the Service Portals should The Subline deem this necessary.</p>
+
+            <p>4. Obligations of The Subline</p>
+
+            <p>4.1 Subject to clause 4.2 below, The Subline will:</p>
+
+            <p>a. list the Merchant Venue and publish its Menu by means of the Service Portals;</p>
+
+            <p>b_ communicate Orders to the Merchant Venue; and</p>
+
+            <p>c. provide reporting on Orders processed via the service.</p>
+
+            <p>4.2 The Subline will use reasonable endeavors to accurately display on the Service Portals information relevant for the Merchants provided by the Merchant or by POS services.</p>
+
+            <p>4.3 The Subline will work with the Merchant to provide quality information and data that The Subline reasonably requires in order to supply the Service.</p>
+
+            <p>5. Obligations of the Merchant Venue</p>
+
+            <p>5.1 The Merchant Venue is responsible for ensuring that at all times it has a functional telephone and, where agreed with The Subline, a facsimile machine, or means of receiving electronic communication such as email, in order to provide updates to the Service.</p>
+
+            <p>5.2 The Merchant Venue must as promptly as possible:</p>
+
+            <p>a. review each Order communicated to it by The Subline;</p>
+
+            <p>b. communicate Order Acceptance to Customers; and</p>
+
+            <p>c. execute Orders promptly and in accordance with a Customer's reasonable expectations.</p>
+
+            <p>5.3 The Merchant Venue acknowledges that:</p>
+
+            <p>a. no contract exists, or at any time will exist, between The Subline and the Merchant for the supply of Raw Materials or Ingredients;</p>
+
+            <p>b. that the sole and exclusive obligation to purchase Raw Materials and Inventory is owed by the Merchant Venue;</p>
+
+            <p>c. the Merchant Venue indemnifies The Subline against any claim or demand made or cost, loss or liability suffered by the Merchant arising directly or indirectly from wasted or lost Raw Materials or Inventory;</p>
+
+            <p>5.5 The Merchant Venue must ensure that the information it provides to The Subline is current and correct, including, but not limited to, its company and/or business name, address, contact telephone number, manager/contact person details, and other relevant information, and the Merchant Venue agrees that it:</p>
+
+            <p>a. must immediately notify The Subline if any information it is required to supply in accordance with this clause changes or becomes inaccurate or incorrect; and</p>
+
+            <p>b. will indemnify The Subline against any claim, loss, liability or damage arising out of any error or inaccuracy of, or any delay in notifying The Subline of any change to, any of the information it is required to supply in accordance with this clause.</p>
+
+            <p>5.6 The Subline reserves the right to:</p>
+
+            <p>a. regularly carry out inspections to ensure compliance with this clause 5; and</p>
+
+            <p>b. immediately suspend or terminate (at The Subline's sole discretion) the supply of the Service if The Subline reasonably suspects the Merchant Venue has not complied with any requirement of this clause 5.</p>
+
+            <p>6. License to use Merchant Venue Material</p>
+
+            <p>6.1 The Merchant Venue grants to The Subline an unrestricted, Worldwide, royalty-free license during the term of this Agreement to use, Venue Products, modify and adapt all Merchant Venue Material for the purposes of:</p>
+
+            <p>a. inclusion on the Service Portals and as may be otherwise required for the proper supply of the Services; and</p>
+
+            <p>b. the general promotion of the Service and Merchant Venue, including without limitation:</p>
+
+            <p>i. use of the Venue's Name for internet advertising purposes, including Google Adwords, to support advertising campaigns and domain registrations for the Service Portals and The Subline; and</p>
+
+            <p>ii. subject to clause 6.4 or unless otherwise agreed in writing, the registration in the United States of America or any other jurisdiction of domain names incorporating any part of the Merchant Venue Material, or material similar to the Venue Material.</p>
+
+            <p>iii. the right to register on google maps, google locations or any other third-party directory websites or services.</p>
+
+            <p>6.2 For the avoidance of doubt, unless otherwise stated or the parties otherwise agreed in writing, the Merchant Venue acknowledges and agrees that, subject to clause 6.4, The Subline may use Merchant Venue Material to register a domain name and operate a website in such a way that a Customer may reasonably assume such website is operated by or on behalf of the Merchant Venue, including by using a domain name the same or similar to the business name of the Merchant Venue and diverting website traffic to a Service Portal, and the Merchant Venue explicitly authorizes the operating of such website by The Subline for the purpose of promoting the sale of Venue</p>
+
+            <p>Products using the Service.</p>
+
+            <p>6.3 Any material the Merchant Venue transmits or submits to The Subline either through the Service Portals or otherwise (&quot;Communicated Material'') shall be considered and may be treated by The Subline as non-confidential, subject to The Subline's obligations under relevant legislation. The Merchant Venue grants to The Subline a royalty-free, perpetual, irrevocable, non-exclusive license to use, copy, modify, adapt, translate, publish and distribute world-wide any Communicated Material for the purposes of providing services under this Agreement or to or for the purposes of advertising and promotion of the Service Portals. The Merchant Venue agrees that all information provided to The Subline that is published, may be relied upon and viewed by Customers to enable them to make decisions and form a legally binding contract with the Merchant Venue.</p>
+
+            <p>7. Operational Information</p>
+
+            <p>7.1 The Merchant Venue is responsible for updating/maintaining Recipes, Inventory Stores, and Inventory Purchases, and any other data inputs that the Service requires. If the Merchant has questions or requires clarification about the The Service, the Merchant must reach out to The Subline for assistance.</p>
+
+            <p>8. Payment</p>
+
+            <p>8.1 The Merchant Venue must pay The Subline the &ldquo;The Subline Service Fee&rdquo; set out in the Subscription Plan.</p>
+
+            <p>8.2 The Subline Service Fee be payable on a monthly basis starting from the &ldquo;The Subline Service Fee&rdquo; Start Date.</p>
+
+            <p>8.3 The merchant venue agrees to provide payment details to The Subline or the channel via which The Subline services was signed up, for direct deduction of all necessary The Subline Service Fees.</p>
+
+            <p>9. Confidentiality</p>
+
+            <p>9.1 A party will not, except with the written consent of the other party or where required to do so by law or stock exchange regulation, disclose any confidential information of the other party.</p>
+
+            <p>9.2 Confidential information of The Subline includes without limitation its business strategies, pricing, revenues, expenses, and order information.</p>
+
+            <p>9.3 Both parties agree to treat as strictly confidential the contents of the Subscription Plan and all other information, data and facts that may be shared between both parties during the course of this Agreement.</p>
+
+            <p>10. Warranty and Indemnity</p>
+
+            <p>10.1 The Merchant Venue warrants that if the Merchant Venue ceases business, closes operations for a material period or is otherwise unable to offer Venue Products to Customers or to satisfy any obligation to Customers, Merchant Venue will immediately inform The Subline.</p>
+
+            <p>10.2 The Subline does not guarantee or warrant that the Service Portals, software, hardware or services will be free from defects or malfunctions. If errors occur, The Subline will use its best endeavors to resolve these as quickly as possible.</p>
+
+            <p>10.3 The Merchant Venue indemnifies and holds harmless The Subline (and its directors, officers, agents, representatives and employees) from and against any and all claims, suits, liabilities, judgments, losses and damages arising out of or in connection with any claim or suit or demand:</p>
+
+            <p>a. arising from any failure to provide notice in accordance with clause 10.1;</p>
+
+            <p>b. by a Customer (or any party on whose behalf a Customer has been acting) in respect of, arising out of, or in connection with, Services;</p>
+
+            <p>c. in any way associated with Venue Products;</p>
+
+            <p>d. compliance with food quality laws or regulations; or</p>
+
+            <p>e. any relevant liquor licensing laws or regulations, except and to the extent any liability, loss or damage arises from the reckless or malicious act or omission of The Subline.</p>
+
+            <p>11. Term and Termination</p>
+
+            <p>11.1 This Agreement starts on the date the Merchant Venue downloads the The Subline Venue Application and accepts these terms and unless terminated earlier under this clause will continue indefinitely.</p>
+
+            <p>11.2 Either party may terminate this Agreement for convenience without giving cause at any time upon 10 days prior written notice to the other party. Following any such termination for convenience:</p>
+
+            <p>a. the Merchant Venue's obligations under this Agreement will continue until the end of the month during which notice is given; and</p>
+
+            <p>b. the Merchant Venue's online listing and promotion undertaken by The Subline will be ceased as soon as is practicable.</p>
+
+            <p>11.3 The Subline may terminate this Agreement, with immediate effect if the Merchant Venue:</p>
+
+            <p>a. provides any inaccurate information about its business to The Subline, such as inaccurate information relating to opening hours, delivery areas, delivery terms or prices;</p>
+
+            <p>b. fails to deliver an Accepted Order to any Customer (except where the Merchant Venue demonstrates it reasonably believed the Accepted Order was fraudulent or the Customer did not intend to, or refused to pay, the Order Price); or</p>
+
+            <p>c. is subject to any event of insolvency (such as the appointment of an administrator, receiver or liquidator or fails to pay its debts as and when they fall due) or bankruptcy (such as having bankruptcy proceedings commenced against the Merchant Venue or being unable to pay any of its creditors).</p>
+
+            <p>11.4 Either party may terminate this Agreement with immediate effect by notice in writing to the other party if the other party commits a material breach of this Agreement not capable of remedy, or in the case of a material breach capable of remedy, is not remedied within 3 business days after written notice is given to the breaching party, specifying the breach.</p>
+
+            <p>11.5 Termination of this Agreement shall not affect the accrued rights or liabilities of the parties at the date of termination.</p>
+
+            <p>12. Limitation of Liability</p>
+
+            <p>12.1 Except for liability in relation to breach of any Non-excludable Condition and liability under clause 12.3, The Subline's total liability to the Merchant Venue in contract, including for one or more breaches of any express term or terms (including any indemnity) of this Agreement (in aggregate), tort (including in negligence), statute, or otherwise, is limited to an amount equal to the total amount of The Subline Service Fee paid by the Merchant Venue to The Subline under this Agreement during the 3 month period before the liability arose.</p>
+
+            <p>12.2 The Subline's total liability to the Merchant Venue for a breach of any Non-excludable Condition (other than a Non-excludable Condition that by law cannot be limited) is limited, at The Subline's option to any one of resupplying, replacing or repairing, or paying the cost of resupplying, replacing or repairing the goods in respect of which the breach occurred, or supplying again or paying the cost of supplying again, the services in respect of which the breach occurred.</p>
+
+            <p>12.3 Except for liability in relation to breach of any Non-excludable Condition, The Subline excludes all liability to the Merchant Venue for lost profits, lost revenue, lost savings, lost business, loss of opportunity, lost data or any consequential or indirect loss arising out of, or in connection with, any services (including the Service), and any claims by any third person (including any Customer), or this Agreement, even if:</p>
+
+            <p>a. The Subline knew that loss was possible; or</p>
+
+            <p>b. the loss was otherwise foreseeable.</p>
+
+            <p>12.4 The Merchant Venue acknowledges that The Subline may make facilities available on the Service Portals for the access by Customers to ratings and reviews of suppliers of goods and services, which may include reviews or ratings of the Merchant Venue, and The Subline will have no liability to the Merchant Venue or any other person for any reason whatsoever arising from any comment, review, assessment or statement (whether true or untrue) made or published by any third person about the Merchant Venue or any person or entity associated with the Merchant Venue.</p>
+
+            <p>13. Dispute resolution</p>
+
+            <p>13.1 Any dispute arising in connection with this Agreement must be handled in accordance with this clause before a party may commence any form of litigation or legal proceedings.</p>
+
+            <p>13.2 A party must give notice to the other party in writing of the nature of any dispute, and within 5 days of such notice:</p>
+
+            <p>a. each party must appoint a representative with full decision making authority to negotiate on behalf of, and bind, their party to resolution of the dispute, and those representatives must meet personally (or, if agreed, by telephone, video conference or such other means as the parties consider appropriate) to consider and seek to resolve the dispute within 5 days of their appointment;</p>
+
+            <p>b. if the respective representatives are unable to resolve the dispute after 5 days of their first meeting (or other such period as is agreed between the parties), refer the dispute to the respective chief executive officers (or equivalent) of each party, who must meet personally (or, if agreed, by telephone, video conference or such other means as the parties consider appropriate) within 7 days to discuss and seek to resolve the dispute; and</p>
+
+            <p>c. if the respective chief executive officers (or equivalent) are unable to resolve the dispute within 7 days of their first meeting, either party is free to commence such process, including alternative dispute resolution or litigation, as they see fit to resolve the dispute.</p>
+
+            <p>14. Variation</p>
+
+            <p>14.1 These terms and conditions may be amended by The Subline at any time by posting revised terms and conditions online at The Subline.com/merchant-terms/, and, subject to clause 18.2, those amended terms and conditions will be effective immediately on posting, and by continuing to use the Service the Merchant Venue will be deemed to have accepted the amended terms and conditions.</p>
+
+            <p>14.2 If the Merchant Venue does not accept any variation to these terms and conditions, it may terminate the Agreement with immediate effect, provided that the Merchant Venue gives notice in writing of such termination to The Subline within 7 days of the amended terms and conditions becoming effective.</p>
+
+            <p>14.3 This Agreement and the Subscription Plan may be amended by The Subline at any time by giving notice in writing (&quot;Amendment Notice&quot;) of the amended terms to the Merchant Venue, and subject to clause 18.4, those amended terms will be effective immediately from the date of the Amendment Notice. Unless the Merchant Venue terminates the Agreement within 7 days after the date of the Amendment Notice in accordance with clause 18.4, the Merchant Venue will be deemed to have accepted the amended terms. If the Merchant Venue terminates the Agreement in accordance with clause 18.4, the amendments detailed in the Amendment Notice will not be binding on the Merchant Venue.</p>
+
+            <p>14.4 If the Merchant Venue does not accept any variation to the Agreement or Subscription Plan, it may terminate the Agreement with immediate effect, provided that the Merchant Venue gives notice in writing of such termination to The Subline within 7 days of the amended terms becoming effective.</p>
+
+            <p>14.5 Except where otherwise explicitly permitted by a clause of this Agreement, the provisions of this Agreement may not be varied by the Merchant Venue, except by agreement in writing signed by the parties.</p>
+
+            <p>15. General</p>
+
+            <p>15.1 For the purposes of this Agreement, any notice required to be given in writing may be given by electronic means, including by email or such other form of written communication as the parties agree from time to time.</p>
+
+            <p>15.2 This Agreement contains all the terms agreed between the parties regarding its subject matter and supersedes and excludes any prior agreement, understanding or arrangement between the parties, whether oral or in writing. No representation, undertaking or promise shall be taken to have been given or be implied from anything said or written in negotiations between the parties prior to this Agreement except as expressly stated in this Agreement.</p>
+
+            <p>15.3 The failure of either party to assert any of its rights under the Agreement, including, but not limited to, the right to terminate the Agreement in the event of breach or default by the other party, will not be deemed to constitute a waiver by that party of its right thereafter to enforce each and every provision of this Agreement in accordance with their terms.</p>
+
+            <p>15.4 The invalidity or unenforceability of any term of or right arising pursuant to this Agreement shall not adversely affect the validity or enforceability of the remaining terms and rights.</p>
+
+            <p>15.5 The Merchant Venue must not assign, transfer, charge or otherwise encumber, create any trust over or deal in any manner with this Agreement or any right, benefit or interest under it, nor transfer, novate or sub-contract any of Merchant Venue's obligations under it.</p>
+
+            <p>15.6 The Subline may assign or novate part or all of this Agreement to any party at any time, and the Merchant Venue:</p>
+
+            <p>a. consents to the transfer or disclosure of its personal Information and this Agreement to any purchaser of the business of The Subline or its assets if that outcome occurs;</p>
+
+            <p>b. hereby acknowledges its consent to such assignment or novation to any party; and</p>
+
+            <p>c. agrees to do all things reasonably required by The Subline, including executing an appropriate deed of assignment or novation, as The Subline reasonably requires to give full effect to such assignment or novation.</p>
+
+            <p>15.7 This Agreement does not create any agency, employment, partnership, joint venture, or other joint relationship between The Subline and the Merchant Venue. The Subline and the Merchant Venue are independent contractors and neither has any authority to bind the other. For the avoidance of doubt, The Subline has no authority to bind the Merchant Venue to any contract with a Customer, and no contract for the supply of Venue Products is formed between any party until the Merchant Venue communicates Order Acceptance, at which time a contract is formed solely between the Merchant Venue and the Customer.</p>
+
+            <p>15.8 This Agreement will be governed by and construed in according to the law of the State of Florida, USA, and each party submits unconditionally to the jurisdiction of the courts of that State.</p>
+
+            <p>16. Security Overview</p>
+
+            <p>16.1 All Merchant and Customer data is written to multiple disks instantly, backed up daily, and stored in multiple locations. Files that Customers upload are stored on servers that use modern techniques to remove bottlenecks and points of failure.</p>
+
+            <p>16.2 Whenever data is in transit between Merchant and The Subline, everything is encrypted, and sent using HTTPS.</p>
+
+            <p>16.3 All credit card transactions are processed using secure encryption are handled via Third Party Payment Providers.</p>
+        </div>
+
+        <script>
+            document.getElementById("helpLink").classList.remove("headerHide");
+            document.getElementById("privacyPolicyLink").classList.remove("headerHide");
+        </script>
+    </body>
+</html>

+ 10 - 2
views/landingPage/landing.ejs

@@ -6,6 +6,7 @@
         <link rel="icon" type="img/png" href="/shared/images/logo.png">
         <link rel="stylesheet" href="/landingPage/landing.css">
         <link rel="stylesheet" href="/shared/shared.css">
+        <link href="https://fonts.googleapis.com/css?family=Saira&display=swap" rel="stylesheet">
     </head>
     <body>
         <% include ../shared/header %>
@@ -70,8 +71,8 @@
                         <span class="checkmark"></span>
         
                         <p style="padding-top: 5px; padding-left: 7px;">I agree to the 
-                            <a class="link" href="/information">Privacy Policy</a> and the 
-                            <a class="link" href="/information">Terms and Conditions</a>
+                            <a class="link" href="/privacy">Privacy Policy</a> and the 
+                            <a class="link" href="/terms">Terms and Conditions</a>
                         </p>
                     </label>   
                 </div>
@@ -125,5 +126,12 @@
         <script src="/landingPage/login.js"></script>
         <script src="/landingPage/register.js"></script>
         <script src="/landingPage/controller.js"></script>
+        <script>
+            let links = document.querySelector(".headerEnd").children;
+
+            for(let i = 0; i < links.length; i++){
+                links[i].classList.remove("headerHide");
+            }
+        </script>
     </body>
 </html>

+ 3 - 4
views/shared/footer.ejs

@@ -3,9 +3,8 @@
 <div class="footer">
     <p>The Subline</p>
     <div>
-        <a href="/information">Privacy Policy</a>
-        <a href="/information">Terms and Conditions</a>
-        <a href="/information">Help</a>
-        <a href="/information">Contact us</a>
+        <a href="/privacy">Privacy Policy</a>
+        <a href="/terms">Terms and Conditions</a>
+        <a href="/help">Help</a>
     </div>
 </div>

+ 11 - 5
views/shared/header.ejs

@@ -1,12 +1,18 @@
 <div class="header">
-    <a class="logo" href="/" >
+    <a class="headerStart" href="/" >
         <img src="/shared/images/logo.png" alt="Subline">
-        <div class='header-logo'> SUBLINE </div>
+        <div class='headerLogo'>THE SUBLINE</div>
     </a>
 
-    <div class="public-buttons">
-        <button id="logInButton" class="button" onclick="loginObj.display()"> Log In </button>
+    <div class="headerEnd">
+        <a id="helpLink" class="headerHide" href="/help">HELP</a>
 
-        <a id="joinButton" class="button-join link" onclick="registerObj.display()"> Or Join </a>
+        <a id="privacyPolicyLink" class="headerHide" href="/privacy">PRIVACY POLICY</a>
+
+        <a id="termsLink" class="headerHide" href="/terms">TERMS AND CONDITIONS</a>
+
+        <button id="logInButton" class="button headerHide" onclick="loginObj.display()">LOG IN</button>
+
+        <button id="joinButton" class="button headerHide" onclick="registerObj.display()">JOIN</button>
     </div>
 </div>

+ 26 - 29
views/shared/shared.css

@@ -232,46 +232,43 @@ form{
     height: 75px;
 }
 
-    .header .logo{
+    .headerHide{
+        display: none;
+    }
+
+    .headerStart{
         display: flex;
-        align-items: center;
+        align-items:center;
         text-decoration: none;
-        margin-left: 20px;
+        margin-left: 25px;
+        width: 275px;
     }
 
-    .header-logo{
-        display: inline-block;
-        color: rgb(255, 99, 107);
-        font-size: 25px;
-        font-weight: 600;
-    }
+        .headerStart img{
+            max-height: 75%;
+        }
 
-    .header img{
-        display: inline-block;
-        max-height: 40px;
-        margin: 15px;
-        text-align: center;
-    }
+        .headerLogo{
+            color: white;
+            font-size: 25px;
+            margin-left: 10px;
+        }
 
-    .header h1{
-        display: inline-block;
-        color: rgb(255, 99, 107);
-    }
-
-    .header .logout{
+    .headerEnd{
         display: flex;
         align-items: center;
-        color: rgb(255, 99, 107);
-        float: right;
+        justify-content: flex-end;
         margin-right: 25px;
-        text-decoration: none;
+        
     }
 
-    .button-join {
-        font-size: medium;
-        color:white;
-        text-decoration: blink;
-    }
+        .headerEnd > *{
+            margin-left: 15px;
+        }
+
+        .headerEnd a{
+            color: rgb(255, 99, 107);
+        }
 
 /* Banner partial */
 .banner{

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor