Prechádzať zdrojové kódy

Merge branch 'validation' into development

Lee Morgan 6 rokov pred
rodič
commit
037d7f350b

+ 5 - 0
models/recipe.js

@@ -18,8 +18,13 @@ const RecipeSchema = new mongoose.Schema({
         },
         quantity: {
             type: Number,
+<<<<<<< HEAD
             min: [0, "Cannot have a negative quantity"],
             required: [true, "Must provide a quantity"]
+=======
+            min: [0, "Quantity cannot be a negative number"],
+            required: true
+>>>>>>> validation
         }
     }]
 });

+ 7 - 21
package-lock.json

@@ -88,14 +88,6 @@
       "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
       "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
     },
-    "async": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
-      "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
-      "requires": {
-        "lodash": "^4.17.11"
-      }
-    },
     "async-each": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
@@ -1637,11 +1629,6 @@
         "package-json": "^4.0.0"
       }
     },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
-    },
     "lowercase-keys": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -1773,17 +1760,16 @@
       }
     },
     "mongoose": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.7.1.tgz",
-      "integrity": "sha512-TJeZHb5h8UGH++ctngh2shgZuZmR9o0D8elxfkWzIpKB6QAFsJPmALtDuD6RYZTS33+CifhDdAMGqs3OpJ3cyQ==",
+      "version": "5.7.4",
+      "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.7.4.tgz",
+      "integrity": "sha512-IgqQS5HIaZ8tG2cib6QllfIw2Wc/A0QVOsdKLsSqRolqJFWOjI0se3vsKXLNkbEcuJ1xziW3e/jPhBs65678Hg==",
       "requires": {
-        "async": "2.6.2",
         "bson": "~1.1.1",
         "kareem": "2.3.1",
         "mongodb": "3.3.2",
         "mongoose-legacy-pluralize": "1.0.2",
         "mpath": "0.6.0",
-        "mquery": "3.2.1",
+        "mquery": "3.2.2",
         "ms": "2.1.2",
         "regexp-clone": "1.0.0",
         "safe-buffer": "5.1.2",
@@ -1809,9 +1795,9 @@
       "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw=="
     },
     "mquery": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.1.tgz",
-      "integrity": "sha512-kY/K8QToZWTTocm0U+r8rqcJCp5PRl6e8tPmoDs5OeSO3DInZE2rAL6AYH+V406JTo8305LdASOQcxRDqHojyw==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz",
+      "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==",
       "requires": {
         "bluebird": "3.5.1",
         "debug": "3.1.0",

+ 1 - 1
package.json

@@ -20,7 +20,7 @@
     "axios": "^0.19.0",
     "ejs": "^2.7.1",
     "express": "^4.17.1",
-    "mongoose": "^5.7.1",
+    "mongoose": "^5.7.4",
     "nodemon": "^1.19.3"
   }
 }

+ 1 - 0
views/inventory/inventory.ejs

@@ -54,6 +54,7 @@
 
         <script>let merchant = <%- JSON.stringify(merchant) %>;</script>
         <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+        <script src="../shared/validation.js"></script>
         <script src="/inventory/inventory.js"></script>
     </body>
 </html>

+ 54 - 33
views/inventory/inventory.js

@@ -1,6 +1,8 @@
-let items = [];
+let items = []; //the ingredients to be displayed
 let tbody = document.querySelector("tbody");
 
+//Remove any existing ingredients in table
+//loop through items and create rows for the table
 let renderIngredients = ()=>{
     while(tbody.hasChildNodes()){
         tbody.removeChild(tbody.firstChild);
@@ -43,11 +45,14 @@ let renderIngredients = ()=>{
     }
 }
 
+//sorts items by specified property
 let sortIngredients = (property)=>{
     items.sort((a, b) => (a[property] > b[property]) ? 1 : -1);
     renderIngredients();
 }
 
+//Empty items list
+//Add ingredients back to items list based on filter input
 let filter = ()=>{
     items = [];
     let searchString = document.querySelector("#filter").value.toLowerCase();
@@ -67,9 +72,11 @@ let filter = ()=>{
     renderIngredients(items);
 }
 
+//Create input allowing for user edit of ingredient
 let editIngredient = (id, row)=>{
     let quantity = row.children[2];
     let button = row.children[4].children[0];
+    let originalQuantity = quantity.innerText;
 
     let quantityInput = document.createElement("input");
     quantityInput.type = "number";
@@ -80,33 +87,41 @@ let editIngredient = (id, row)=>{
     quantity.appendChild(quantityInput);
 
     button.innerText = "Save";
-    button.onclick = ()=>{updateOne(id, row)};
+    button.onclick = ()=>{updateOne(id, row, originalQuantity)};
 }
 
-let updateOne = (id, row)=>{
+//Save user input of ingredient
+//Update both page and database
+let updateOne = (id, row, originalQuantity)=>{
     let quantityField = row.children[2];
     let quantity = quantityField.children[0].value;
     let button = row.children[4].children[0];
 
     quantityField.removeChild(quantityField.firstChild);
-    quantityField.innerText = quantity;
+
+    if(validator.ingredient.quantity(quantity)){
+        axios.post("/ingredients/update", {
+            id: id,
+            quantity: quantity
+        })
+            .then((ingredient)=>{
+                banner.createNotification("The ingredient has been successfully updated");
+            })
+            .catch((err)=>{
+                banner.createError("There was an error and the ingredient was not updated");
+                console.log(err);
+            });
+
+        quantityField.innerText = quantity;
+    }else{
+        quantityField.innerText = originalQuantity;
+    }
 
     button.innerText = "Edit";
     button.onclick = ()=>{editIngredient(id, row)};
-
-    axios.post("/ingredients/update", {
-        id: id,
-        quantity: quantity
-    })
-        .then((ingredient)=>{
-            banner.createNotification("The ingredient has been successfully updated");
-        })
-        .catch((err)=>{
-            banner.createError("There was an error and the ingredient was not updated");
-            console.log(err);
-        });
 }
 
+//Delete an ingredient from both the page and the database
 let removeIngredient = (id, row)=>{
     axios.post("/ingredients/remove", {id: id})
         .then((merchant)=>{
@@ -124,6 +139,7 @@ let removeIngredient = (id, row)=>{
         });
 }
 
+//Display the modal to allow for adding a new ingredient
 let displayAdd = ()=>{
     let modal = document.querySelector(".add-ingredient");
 
@@ -134,6 +150,8 @@ let displayAdd = ()=>{
     modal.style.visibility = "visible";
 }
 
+//Update new ingredient on both the page and the database
+//Close the modal
 let addIngredient = ()=>{
     let content = document.querySelector(".modal-content");
 
@@ -146,27 +164,30 @@ let addIngredient = ()=>{
         quantity: content.children[2].children[0].value
     }
 
-    axios.post("/ingredients/createone", newIngredient)
-        .then((ingredient)=>{
-            items.push({
-                id: ingredient._id,
-                name: newIngredient.ingredient.name,
-                category: newIngredient.ingredient.category,
-                quantity: newIngredient.quantity,
-                unit: newIngredient.ingredient.unitType
+    if(validator.ingredient.all(newIngredient.ingredient, newIngredient.quantity)){
+        axios.post("/ingredients/createone", newIngredient)
+            .then((ingredient)=>{
+                items.push({
+                    id: ingredient._id,
+                    name: newIngredient.ingredient.name,
+                    category: newIngredient.ingredient.category,
+                    quantity: newIngredient.quantity,
+                    unit: newIngredient.ingredient.unitType
+                })
+                
+                sortIngredients("name");
+                renderIngredients();
+                banner.createNotification("The new ingredient has been successfully added to your inventory");
             })
-            
-            sortIngredients("name");
-            renderIngredients();
-            banner.createNotification("The new ingredient has been successfully added to your inventory");
-        })
-        .catch((err)=>{
-            banner.createError("There was an error and the ingredient could not be added to your inventory");
-            console.log(err);
-        });
+            .catch((err)=>{
+                banner.createError("There was an error and the ingredient could not be added to your inventory");
+                console.log(err);
+            });
+    }
 
     let modal = document.querySelector(".add-ingredient");
     modal.style.visibility = "hidden";
 }
 
+//Initial run 
 filter();

+ 18 - 0
views/merchantSetupPage/controller.js

@@ -0,0 +1,18 @@
+let data = {}; //For storing all data from user to pass to backend
+
+//Divs to switch out to show different pages
+let addIngredients = document.querySelector("#addIngredients");
+let newIngredients = document.querySelector("#newIngredients");
+let createRecipes = document.querySelector("#createRecipes");
+
+//General purpose  data validator
+let checkValid = (valueToCheck, inputField)=>{
+    if(!validator.ingredient[valueToCheck](inputField.value, createBanner = false)){
+        inputField.classList += " input-error"
+    }else{
+        inputField.classList.remove("input-error");
+    }
+}
+
+//Run first function
+ingredientSetup.populateIngredients();

+ 189 - 0
views/merchantSetupPage/ingredientSetup.js

@@ -0,0 +1,189 @@
+let ingredientSetup = {
+    existingIngredientElements: [], // each object in list is a full tr for one ingredient
+    newIngredientElements: [],  // each object in list is a full tr for one ingredient
+
+    //Loops through all ingredients passed from database
+    //Creates a row for each ingredient and adds it to table
+    populateIngredients: function(){
+        let tBody = document.createElement("tbody");
+    
+        for(let ingredient of ingredients){
+            let row = document.createElement("tr");
+            row.id = ingredient._id;
+        
+            let add = document.createElement("td");
+            let checkbox = document.createElement("input");
+            checkbox.type = "checkbox";
+            add.appendChild(checkbox);
+            row.appendChild(add);
+        
+            let name = document.createElement("td");
+            name.innerText = ingredient.name;
+            row.appendChild(name);
+        
+            let category = document.createElement("td");
+            category.innerText = ingredient.category;
+            row.appendChild(category);
+        
+            let quantity = document.createElement("td");
+            let quantityInput = document.createElement("input");
+            quantityInput.type = "number";
+            quantityInput.step = "0.01";
+            quantityInput.min = "0";
+    
+            quantity.appendChild(quantityInput);
+            row.appendChild(quantity);
+            let unit = document.createElement("td");
+            unit.innerText = ingredient.unitType;
+            row.appendChild(unit);
+        
+            let idField = document.createElement("input");
+            idField.type = "hidden";
+            idField.value = ingredient._id;
+        
+            tBody.appendChild(row);
+            let oldTBody = document.querySelector("#ingredient-display tbody");
+            oldTBody.parentNode.replaceChild(tBody, oldTBody);
+            this.existingIngredientElements.push(row);
+
+            this.displayExistingIngredients();
+        }
+    },
+
+    //Display existing ingredients table
+    //Hide other tables
+    displayExistingIngredients: function(){
+        addIngredients.style.display = "flex";
+        newIngredients.style.display = "none";
+        createRecipes.style.display = "none";
+    },
+
+    //Display new ingredients table
+    //Hide other tables
+    displayNewIngredients: function(){
+        addIngredients.style.display = "none";
+        newIngredients.style.display = "flex";
+        createRecipes.style.display = "none";
+    },
+
+    //Creates a new, empty row in table to input data
+    newIngredientField: function(){
+        let body = document.querySelector("#inputField tbody");
+        let row = document.createElement("tr");
+    
+        let name = document.createElement("td");
+        let nameInput = document.createElement("input");
+        nameInput.type = "text";
+        nameInput.onblur = ()=>{checkValid("name", nameInput)};
+        name.appendChild(nameInput);
+        row.appendChild(name);
+    
+        let category = document.createElement("td");
+        let categoryInput = document.createElement("input");
+        categoryInput.type = "text"
+        categoryInput.onblur = ()=>{checkValid("category", categoryInput)};
+        category.appendChild(categoryInput);
+        row.appendChild(category);
+    
+        let quantity = document.createElement("td");
+        let quantityInput = document.createElement("input");
+        quantityInput.type = "number";
+        quantityInput.step = "0.01";
+        quantityInput.onblur = ()=>{checkValid("quantity", quantityInput)};
+        quantity.appendChild(quantityInput);
+        row.appendChild(quantity);
+    
+        let unit = document.createElement("td");
+        let unitInput = document.createElement("input");
+        unitInput.type = "text";
+        unitInput.onblur = ()=>{checkValid("unit", unitInput)};
+        unit.appendChild(unitInput);
+        row.appendChild(unit);
+    
+        let removeTd = document.createElement("td");
+        let removeButton = document.createElement("button");
+        removeButton.innerText = "-";
+        removeButton.onclick = ()=>{this.removeRow(row)};
+        removeTd.appendChild(removeButton);
+        row.appendChild(removeTd);
+    
+        body.appendChild(row);
+        this.newIngredientElements.push(row);
+    },
+
+    //Remove row from new ingredients table
+    removeRow: function(row){
+        for(let i = 0; i < this.newIngredientElements.length; i++){
+            if(this.newIngredientElements[i] === row){
+                this.newIngredientElements.splice(i, 1);
+            }
+        }
+        row.parentNode.removeChild(row);
+    },
+
+    //refactor
+    //nothin should run unless everything is valid
+    createIngredientsList: function(){
+        data.ingredients = [];
+        for(let ingredient of this.existingIngredientElements){
+            if(ingredient.children[0].children[0].checked){
+                data.ingredients.push({
+                    id: ingredient.id,
+                    name: ingredient.children[1].textContent,
+                    quantity: ingredient.children[3].children[0].value,
+                    unitType: ingredient.children[4].textContent
+                });
+            }
+        }
+    
+        let newIngredient = [];
+        let newIngredientQuantity = [];
+        for(let ingredient of this.newIngredientElements){
+            newIngredient.push({
+                name: ingredient.children[0].children[0].value,
+                category: ingredient.children[1].children[0].value,
+                unitType: ingredient.children[3].children[0].value
+            });
+            newIngredientQuantity.push({
+                name: ingredient.children[0].children[0].value,
+                quantity: ingredient.children[2].children[0].value
+            });
+        }
+    
+        let isValid = true;
+        for(let i = 0; i < newIngredient.length; i++){
+            if(!validator.ingredient.all(newIngredient[i], newIngredientQuantity[i].quantity)){
+                isValid = false;
+                data.ingredients = [];
+                break;
+            }
+        }
+    
+        if(isValid){
+            axios.post("/ingredients/create", newIngredient)
+                .then((result)=>{
+                    for(let ingredient of result.data){
+                        let newIngredient = {
+                            id: ingredient._id,
+                            name: ingredient.name,
+                            unitType: ingredient.unitType
+                        }
+    
+                        for(let item of newIngredientQuantity){
+                            if(ingredient.name === item.name){
+                                newIngredient.quantity = item.quantity;
+                            }
+                        }
+    
+                        data.ingredients.push(newIngredient);
+                    }
+                    banner.createNotification("All ingredients have been created and added to your inventory");
+                    recipeSetup.createRecipePage();
+                })
+                .catch((err)=>{
+                    banner.createError("There has been an error and your ingredients have not been saved");
+                    console.log(err);
+                });
+        }
+    }
+};

+ 5 - 1
views/merchantSetupPage/merchantSetup.css

@@ -55,4 +55,8 @@ table{
         .input-new{
             display: flex;
             flex-wrap: nowrap;
-        }
+        }
+
+.input-error{
+    border-color: red;
+}

+ 10 - 7
views/merchantSetupPage/merchantSetup.ejs

@@ -29,7 +29,7 @@
                 <tbody></tbody>
             </table>
 
-            <button onclick="updateState(1)">Next</button>
+            <button onclick="ingredientSetup.displayNewIngredients()">Next</button>
         </div>
 
         <div id="newIngredients" class="container hide">
@@ -49,9 +49,9 @@
                 </thead>
                 <tbody></tbody>
             </table>
-            <button onclick="newIngredientField()">+</button>
-            <button onclick="updateState(1)">Create Ingredients</button>
-            <button onclick="updateState(-1)">Back</button>
+            <button onclick="ingredientSetup.newIngredientField()">+</button>
+            <button onclick="ingredientSetup.createIngredientsList()">Create Ingredients</button>
+            <button onclick="ingredientSetup.displayExistingIngredients()">Back</button>
         </div>
 
         <div id="createRecipes" class="container hide">
@@ -70,9 +70,9 @@
                 <tbody></tbody>
             </table>
 
-            <button onclick="addRecipeIngredientField()">+</button>
+            <button onclick="recipeSetup.addRecipeIngredientField()">+</button>
             <button id="next"></button>
-            <button id="previous" onclick="changeRecipe(-1)">Previous Recipe</button>
+            <button id="previous" onclick="recipeSetup.changeRecipe(-1)">Previous Recipe</button>
         </div>
 
         <script>
@@ -80,7 +80,10 @@
             let recipes = <%- JSON.stringify(recipes) %>;
         </script>
         <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
-        <script src="/merchantSetupPage/merchantSetup.js"></script>
+        <script src="../shared/validation.js"></script>
+        <script src="/merchantSetupPage/recipeSetup.js"></script>
+        <script src="/merchantSetupPage/ingredientSetup.js"></script>
+        <script src="/merchantSetupPage/controller.js"></script>
     </body>
 </html>
 

+ 0 - 331
views/merchantSetupPage/merchantSetup.js

@@ -1,334 +1,3 @@
-let state = 0;
-let data = {};
 
-let addIngredients = document.querySelector("#addIngredients");
-let newIngredients = document.querySelector("#newIngredients");
-let createRecipes = document.querySelector("#createRecipes");
-let existingIngredientElements = [];
-let newIngredientElements = [];
-let recipeData = [];
-let recipeDataIndex = 0;
 
-for(let recipe of recipes.elements){
-    recipeData.push(
-        {
-            id: recipe.id,
-            name: recipe.name,
-            ingredients: []
-        }
-    )
-}
 
-let updateState = (num)=>{
-    state += num;
-    if(state === 0){
-        addIngredients.style.display = "flex";
-        newIngredients.style.display = "none";
-        createRecipes.style.display = "none";
-    }else if(state === 1){
-        addIngredients.style.display = "none";
-        newIngredients.style.display = "flex";
-        createRecipes.style.display = "none";
-    }else if(state === 2){
-        addIngredients.style.display = "none";
-        newIngredients.style.display = "none";
-        createRecipes.style.display = "flex";
-        createIngredientsList();
-    }
-}
-
-//Ingredient functions
-let populateIngredients = ()=>{
-    let tBody = document.createElement("tbody");
-
-    for(let ingredient of ingredients){
-        let row = document.createElement("tr");
-        row.id = ingredient._id;
-    
-        let add = document.createElement("td");
-        let checkbox = document.createElement("input");
-        checkbox.type = "checkbox";
-        add.appendChild(checkbox);
-        row.appendChild(add);
-    
-        let name = document.createElement("td");
-        name.innerText = ingredient.name;
-        row.appendChild(name);
-    
-        let category = document.createElement("td");
-        category.innerText = ingredient.category;
-        row.appendChild(category);
-    
-        let quantity = document.createElement("td");
-        let quantityInput = document.createElement("input");
-        quantityInput.type = "number";
-        quantityInput.step = "0.01";
-        quantityInput.min = "0";
-
-        quantity.appendChild(quantityInput);
-        row.appendChild(quantity);
-        let unit = document.createElement("td");
-        unit.innerText = ingredient.unitType;
-        row.appendChild(unit);
-    
-        let idField = document.createElement("input");
-        idField.type = "hidden";
-        idField.value = ingredient._id;
-    
-        tBody.appendChild(row);
-        let oldTBody = document.querySelector("#ingredient-display tbody");
-        oldTBody.parentNode.replaceChild(tBody, oldTBody);
-        existingIngredientElements.push(row);
-    }
-}
-
-let newIngredientField = ()=>{
-    let body = document.querySelector("#inputField tbody");
-    let row = document.createElement("tr");
-
-    let name = document.createElement("td");
-    let nameInput = document.createElement("input");
-    nameInput.type = "text";
-    name.appendChild(nameInput);
-    row.appendChild(name);
-
-    let category = document.createElement("td");
-    let categoryInput = document.createElement("input");
-    categoryInput.type = "text"
-    category.appendChild(categoryInput);
-    row.appendChild(category);
-    let quantity = document.createElement("td");
-    let quantityInput = document.createElement("input");
-    quantityInput.type = "number";
-    quantityInput.step = "0.01";
-    quantity.appendChild(quantityInput);
-    row.appendChild(quantity);
-
-    let unit = document.createElement("td");
-    let unitInput = document.createElement("input");
-    unitInput.type = "text";
-    unit.appendChild(unitInput);
-    row.appendChild(unit);
-
-    let removeTd = document.createElement("td");
-    let removeButton = document.createElement("button");
-    removeButton.innerText = "-";
-    removeButton.onclick = ()=>{removeRow(row)};
-    removeTd.appendChild(removeButton);
-    row.appendChild(removeTd);
-
-    body.appendChild(row);
-    newIngredientElements.push(row);
-}
-
-let removeRow = (row)=>{
-    for(let i = 0; i < newIngredientElements.length; i++){
-        if(newIngredientElements[i] === row){
-            newIngredientElements.splice(i, 1);
-        }
-    }
-    row.parentNode.removeChild(row);
-}
-
-let createIngredientsList = ()=>{
-    data.ingredients = [];
-    for(let ingredient of existingIngredientElements){
-        if(ingredient.children[0].children[0].checked){
-            data.ingredients.push({
-                id: ingredient.id,
-                name: ingredient.children[1].textContent,
-                quantity: ingredient.children[3].children[0].value,
-                unitType: ingredient.children[4].textContent
-            });
-        }
-    }
-
-    let newIngredient = [];
-    let newIngredientQuantity = [];
-    for(let ingredient of newIngredientElements){
-        newIngredient.push({
-            name: ingredient.children[0].children[0].value,
-            category: ingredient.children[1].children[0].value,
-            unitType: ingredient.children[3].children[0].value
-        });
-        newIngredientQuantity.push({
-            name: ingredient.children[0].children[0].value,
-            quantity: ingredient.children[2].children[0].value
-        })
-    }
-
-    axios.post("/ingredients/create", newIngredient)
-        .then((result)=>{
-            for(let ingredient of result.data){
-                let newIngredient = {
-                    id: ingredient._id,
-                    name: ingredient.name,
-                    unitType: ingredient.unitType
-                }
-
-                for(let item of newIngredientQuantity){
-                    if(ingredient.name === item.name){
-                        newIngredient.quantity = item.quantity;
-                    }
-                }
-
-                data.ingredients.push(newIngredient);
-            }
-            banner.createNotification("All ingredients have been created and added to your inventory");
-            showRecipe();
-        })
-        .catch((err)=>{
-            banner.createError("There has been an error and your ingredients have not been saved");
-            console.log(err);
-        });
-}
-
-//Recipe functions
-let showRecipe = ()=>{
-    let title = document.querySelector("#recipeName");
-    title.innerText = recipeData[recipeDataIndex].name;
-
-    let body = document.querySelector("#recipes tbody");
-    for(let ing of recipeData[recipeDataIndex].ingredients){
-        let row = document.createElement("tr");
-        body.appendChild(row);
-
-        let ingTd = document.createElement("td");
-        row.appendChild(ingTd);
-        let ingName = document.createElement("select");
-        for(let ingredient of data.ingredients){
-            let newOption = document.createElement("option");
-            newOption.innerText = ingredient.name;
-            newOption.value = ingredient.id;
-            if(ingredient.id === ing.id){
-                newOption.selected = "selected";
-            }
-            ingName.appendChild(newOption);
-        }
-        ingTd.appendChild(ingName);
-
-        let quantTd = document.createElement("td");
-        row.appendChild(quantTd);
-        let ingQuant = document.createElement("input");
-        ingQuant.type = "number";
-        ingQuant.step = "0.01";
-        ingQuant.value = ing.quantity;
-        quantTd.appendChild(ingQuant);
-    }
-
-    let nextButton = document.querySelector("#next");
-    if(recipeDataIndex === recipeData.length - 1){
-        nextButton.innerText = "Finish";
-        nextButton.onclick = submitAll;
-    }else{
-        nextButton.innerText = "Next Recipe";
-        nextButton.onclick = ()=>{changeRecipe(1)};
-    }
-
-    let previousButton = document.querySelector("#previous");
-    if(recipeDataIndex === 0){
-        previousButton.style.display = "none";
-    }else{
-        previousButton.style.display = "inline-block";
-    }
-}
-
-let addRecipeIngredientField = ()=>{
-    let body = document.querySelector("#recipes tbody");
-
-    let row = document.createElement("tr");
-    body.appendChild(row);
-
-    let ingTd = document.createElement("td");
-    row.appendChild(ingTd);
-    let ingName = document.createElement("select");
-    for(let ingredient of data.ingredients){
-        let newOption = document.createElement("option");
-        newOption.innerText = ingredient.name;
-        newOption.value = ingredient.id;
-        ingName.appendChild(newOption);
-    }
-    ingTd.appendChild(ingName);
-
-    let quantTd = document.createElement("td");
-    row.appendChild(quantTd);
-    let ingQuant = document.createElement("input");
-    ingQuant.type = "number";
-    ingQuant.step = "0.01";
-    ingQuant.min = "0";
-    quantTd.appendChild(ingQuant);
-
-    let removeTd = document.createElement("td");
-    row.appendChild(removeTd);
-    let removeButton = document.createElement("button");
-    removeButton.innerText = "-";
-    removeButton.onclick = ()=>{row.parentNode.removeChild(row)};
-    removeTd.appendChild(removeButton);
-}
-
-let changeRecipe = (num)=>{
-    let body = document.querySelector("#recipes tbody");
-
-    let recipeIngredients = [];
-    while(body.children.length > 0){
-        let row = body.firstChild;
-        recipeIngredients.push({
-            id: row.children[0].children[0].value,
-            quantity: row.children[1].children[0].value
-        });
-        recipeData[recipeDataIndex].ingredients = recipeIngredients;
-
-        body.removeChild(row);
-    }
-    recipeDataIndex += num;
-    showRecipe();
-}
-
-let submitAll = ()=>{
-    let body = document.querySelector("#recipes tbody");
-    data.recipes = [];
-
-    let recipeIngredients = [];
-    while(body.children.length > 0){
-        let row = body.firstChild;
-        recipeIngredients.push({
-            id: row.children[0].children[0].value,
-            quantity: row.children[1].children[0].value
-        });
-        recipeData[recipeDataIndex].ingredients = recipeIngredients;
-
-        body.removeChild(row);
-    }
-
-    for(let recipe of recipeData){
-        let newRecipe = {
-            cloverId: recipe.id,
-            name: recipe.name,
-            ingredients: []
-        };
-        for(let ingredient of recipe.ingredients){
-            newRecipe.ingredients.push({
-                id: ingredient.id,
-                quantity: ingredient.quantity
-            });
-        }
-        data.recipes.push(newRecipe);
-    }
-    
-
-    let form = document.createElement("form");
-    form.method = "post";
-    form.action = "/merchant/create"
-    
-    let dataInput = document.createElement("input");
-    dataInput.type = "hidden";
-    dataInput.name = "data";
-    dataInput.value = JSON.stringify(data);
-
-    form.appendChild(dataInput);
-    document.body.appendChild(form);
-    form.submit();
-}
-
-populateIngredients();
-updateState(0);

+ 198 - 0
views/merchantSetupPage/recipeSetup.js

@@ -0,0 +1,198 @@
+let recipeSetup = {
+    recipeData: [],  //stores data from recipes, including ingredients
+    recipeDataIndex: 0,  //index for recipeData, which one is currently displaying
+
+    //Display recipe page and hide others
+    //Populate recipeData with data from Clover
+    createRecipePage: function(){
+        addIngredients.style.display = "none";
+        newIngredients.style.display = "none";
+        createRecipes.style.display = "flex";
+
+        for(let recipe of recipes.elements){
+            this.recipeData.push(
+                {
+                    id: recipe.id,
+                    name: recipe.name,
+                    ingredients: []
+                }
+            )
+        }
+
+        this.showRecipe();
+    },
+
+    //Loops through recipeData to create td's
+    //Displays each ingredient for current recipe
+    //Create and display correct buttons for navigation
+    showRecipe: function(){
+        let title = document.querySelector("#recipeName");
+        title.innerText = this.recipeData[this.recipeDataIndex].name;
+    
+        let body = document.querySelector("#recipes tbody");
+        for(let ing of this.recipeData[this.recipeDataIndex].ingredients){
+            let row = document.createElement("tr");
+            body.appendChild(row);
+    
+            let ingTd = document.createElement("td");
+            row.appendChild(ingTd);
+            let ingName = document.createElement("select");
+            for(let ingredient of data.ingredients){
+                let newOption = document.createElement("option");
+                newOption.innerText = ingredient.name;
+                newOption.value = ingredient.id;
+                if(ingredient.id === ing.id){
+                    newOption.selected = "selected";
+                }
+                ingName.appendChild(newOption);
+            }
+            ingTd.appendChild(ingName);
+    
+            let quantTd = document.createElement("td");
+            row.appendChild(quantTd);
+            let ingQuant = document.createElement("input");
+            ingQuant.type = "number";
+            ingQuant.step = "0.01";
+            ingQuant.value = ing.quantity;
+            ingQuant.onblur = ()=>{checkValid("quantity", ingQuant)};
+            quantTd.appendChild(ingQuant);
+        }
+    
+        let nextButton = document.querySelector("#next");
+        if(this.recipeDataIndex === this.recipeData.length - 1){
+            nextButton.innerText = "Finish";
+            nextButton.onclick = ()=>{this.submitAll()};
+        }else{
+            nextButton.innerText = "Next Recipe";
+            nextButton.onclick = ()=>{this.changeRecipe(1)};
+        }
+    
+        let previousButton = document.querySelector("#previous");
+        if(this.recipeDataIndex === 0){
+            previousButton.style.display = "none";
+        }else{
+            previousButton.style.display = "inline-block";
+        }
+    },
+
+    //Adds ingredient data to recipeData
+    //Empties all data in the table
+    //Changes recipeDataIndex
+    //Hands off to showRecipe function
+    changeRecipe: function(num){
+        let body = document.querySelector("#recipes tbody");
+        this.recipeData[this.recipeDataIndex].ingredients = [];
+        let isValid = true;
+    
+        for(let row of body.children){
+            newIngredient ={
+                id: row.children[0].children[0].value,
+                quantity: row.children[1].children[0].value
+            };
+
+            this.recipeData[this.recipeDataIndex].ingredients.push(newIngredient);
+            if(!validator.ingredient.quantity(newIngredient.quantity)){
+                isValid = false;
+                break;
+            }
+        }
+
+        if(isValid){
+            while(body.children.length > 0){
+                body.removeChild(body.firstChild);
+            }
+
+            this.recipeDataIndex += num;
+            this.showRecipe();
+        }
+    },
+
+    //Add all recipes to data variable
+    //Creates a form and submits data
+    submitAll: function(){
+        this.recipeData[this.recipeDataIndex].ingredients = [];
+        let body = document.querySelector("#recipes tbody");
+        data.recipes = [];
+        let isValid = true;
+
+        for(let row of body.children){
+            newIngredient ={
+                id: row.children[0].children[0].value,
+                quantity: row.children[1].children[0].value
+            };
+            
+            this.recipeData[this.recipeDataIndex].ingredients.push(newIngredient);
+            if(!validator.ingredient.quantity(newIngredient.quantity)){
+                
+                isValid = false;
+                break;
+            }
+        }
+
+        if(isValid){
+            for(let recipe of this.recipeData){
+                let newRecipe = {
+                    cloverId: recipe.id,
+                    name: recipe.name,
+                    ingredients: []
+                };
+
+                for(let ingredient of recipe.ingredients){
+                    newRecipe.ingredients.push({
+                        id: ingredient.id,
+                        quantity: ingredient.quantity
+                    });
+                }
+                data.recipes.push(newRecipe);
+            }
+            
+            let form = document.createElement("form");
+            form.method = "post";
+            form.action = "/merchant/create"
+            
+            let dataInput = document.createElement("input");
+            dataInput.type = "hidden";
+            dataInput.name = "data";
+            dataInput.value = JSON.stringify(data);
+        
+            form.appendChild(dataInput);
+            document.body.appendChild(form);
+            form.submit();
+        }
+    },
+
+    //Creates a new, empty row in table to input data
+    addRecipeIngredientField: function(){
+        let body = document.querySelector("#recipes tbody");
+    
+        let row = document.createElement("tr");
+        body.appendChild(row);
+    
+        let ingTd = document.createElement("td");
+        row.appendChild(ingTd);
+        let ingName = document.createElement("select");
+        for(let ingredient of data.ingredients){
+            let newOption = document.createElement("option");
+            newOption.innerText = ingredient.name;
+            newOption.value = ingredient.id;
+            ingName.appendChild(newOption);
+        }
+        ingTd.appendChild(ingName);
+    
+        let quantTd = document.createElement("td");
+        row.appendChild(quantTd);
+        let ingQuant = document.createElement("input");
+        ingQuant.type = "number";
+        ingQuant.step = "0.01";
+        ingQuant.min = "0";
+        ingQuant.onblur = ()=>{checkValid("quantity", ingQuant)};
+        quantTd.appendChild(ingQuant);
+    
+        let removeTd = document.createElement("td");
+        row.appendChild(removeTd);
+        let removeButton = document.createElement("button");
+        removeButton.innerText = "-";
+        removeButton.onclick = ()=>{row.parentNode.removeChild(row)};
+        removeTd.appendChild(removeButton);
+    }
+}

+ 39 - 15
views/shared/banner.ejs

@@ -1,31 +1,55 @@
 <div class="banner">
-    <p></p>
+    <ul></ul>
 </div>
 
 <script>
     let banner = {
-        createNotification: (val)=>{
-            let currentBanner = document.querySelector(".banner");
-            let bannerText = document.querySelector(".banner p");
-            currentBanner.style.display = "inline-block";
-            bannerText.innerText = val;
-            bannerText.classList = "notification";
+        errorList: [],
+        notificationList: [],
+
+        createNotification: function(value){
+            this.notificationList.push(value);
+
+            this.updateNotifications();
 
             setTimeout(()=>{
-                currentBanner.style.display = "none";
+                this.notificationList.splice(0, 1);
+                this.updateNotifications();
             }, 10000);
         },
 
-        createError: (val)=>{
-            let currentBanner = document.querySelector(".banner");
-            let bannerText = document.querySelector(".banner p");
-            currentBanner.style.display = "inline-block";
-            bannerText.innerText = val;
-            bannerText.classList = "error";
+        createError: function(value){
+            this.errorList.push(value);
+
+            this.updateNotifications();
 
             setTimeout(()=>{
-                currentBanner.style.display = "none";
+                this.errorList.splice(0, 1);
+                this.updateNotifications();
             }, 10000);
+        },
+
+        updateNotifications: function(){
+            let ul = document.querySelector(".banner ul");
+
+            while(ul.hasChildNodes()){
+                ul.removeChild(ul.firstChild);
+            }
+
+            for(let notification of this.notificationList){
+                let li = document.createElement("li");                                                                       
+                                                                                                            
+                li.classList = "notification";
+                li.innerText = notification;
+                ul.appendChild(li);
+            }
+
+            for(let error of this.errorList){
+                let li = document.createElement("li");
+                li.classList = "error";
+                li.innerText = error;
+                ul.appendChild(li);
+            }
         }
     }
 </script>

+ 3 - 5
views/shared/shared.css

@@ -27,21 +27,19 @@
 .banner{
     width: 100%;
     text-align: center;
-    display: none;
+    min-height: 50px;
 }
 
     .banner .notification{
         background: green;
         color: white;
-        padding: 10px;
-        margin: 5px;
         font-weight: bold;
+        list-style-type: none;
     }
 
     .banner .error{
         background: red;
         color: white;
-        padding: 10px;
-        margin: 5px;
         font-weight: bold;
+        list-style-type: none;
     }

+ 55 - 0
views/shared/validation.js

@@ -0,0 +1,55 @@
+let validator = {
+    ingredient: {
+        name: function(ingName, createBanner = true){
+            if(ingName.length < 2){
+                if(createBanner){
+                    banner.createError("Ingredient name must contain at least 2 characters");
+                }
+                return false;
+            }
+
+            return true;
+        },
+
+        category: function(ingCategory, createBanner = true){
+            if(ingCategory.length < 3){
+                if(createBanner){
+                    banner.createError("Category name must contain at least 3 characters");
+                }
+                return false;
+            }
+
+            return true;
+        },
+
+        quantity: function(num, createBanner = true){
+            if(num < 0){
+                if(createBanner){
+                    banner.createError("Quantity cannot be a negative number");
+                }
+                return false;
+            }
+
+            return true;
+        },
+
+        unit: (ingUnit)=>{
+            return true;
+        },
+
+        //Check all parts of ingredient, return true if all pass
+        //Quantity passed seperately and optional
+        all: function(ingObject, quantity = 0, createBanner = true){
+            let nameCheck = this.name(ingObject.name, createBanner);
+            let categoryCheck = this.category(ingObject.category, createBanner);
+            let unitCheck = this.unit(ingObject.unitType, createBanner);
+            let quantityCheck = this.quantity(Number(quantity), createBanner);
+
+            if(!nameCheck || !categoryCheck || !quantityCheck || !unitCheck){
+                return false;
+            }
+
+            return true;
+        }
+    }
+}