diff --git a/public/StyleSheet.css b/public/StyleSheet.css new file mode 100644 index 00000000..c1c4529b --- /dev/null +++ b/public/StyleSheet.css @@ -0,0 +1,10 @@ +#container { + display: grid; + grid-template-columns: 300px 300px 300px 300px; + grid-template-rows: 300px 300px 300px; +} + +h1 { + text-align: center; + background-color: #AABFEF; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index 138da963..dd7505ef 100644 --- a/public/index.html +++ b/public/index.html @@ -1,5 +1,7 @@ +Essential Pharmaceuticals + @@ -7,8 +9,36 @@ - - - + +

Essential Pharmaceuticals

+

+ +
+
+ + +
+ + +


+
+
+
+ + + + + + + \ No newline at end of file diff --git a/public/index.js b/public/index.js index e69de29b..f2d434c4 100644 --- a/public/index.js +++ b/public/index.js @@ -0,0 +1,105 @@ +//Basic shopping site, ACA JS311 +//Nicolai Antonov +//April 2019 + +//load main products page +//if value exists in searchbox, filter to only include results with that value +//else, if category is selected, filter to show only products in that category +//else, list all products +let loadPage = () => { + //empty the status bar whenever page is loaded + let statusbar = document.getElementById("statusbar") + statusbar.innerHTML = " " + let searchTerm = document.getElementById("searchbox").value + let catSelect = document.getElementById("catSelect").value + + //if local storage does not yet have users sign up info, present sign up screen + if (!localStorage.getItem("user")) { + signUp() + return + //if user exists and does not yet have an assigned cart, create and assign one from the server + } else { + //run initCart to create cart if the current user does not have an assigned cartId + initCart() + } + + let newStr = "" + let tenOptions = "" + if (searchTerm) { + for (let i = 0; i < products.length; i++) { + let product = products[i] + if (product.name.includes(searchTerm) || product.description.includes(searchTerm)) { + newStr += `
${product.name} - ${product.price}
` + `
` + } + } + if (newStr === "") { + newStr = "

No results founds

" + } + } else if (catSelect) { + for (let i = 0; i < products.length; i++) { + let product = products[i] + if (product.category === catSelect) { + newStr += `
${product.name} - ${product.price}
` + `
` + } + } + } else { + for (let i = 0; i < products.length; i++) { + let product = products[i] + newStr += `
${product.name} - ${product.price}
` + `
` + } + } + //place products in container to display all products in a grid + newStr = '
' + newStr + '
' + currentDiv.innerHTML = newStr +} + +//define a function to show product description on page, reached on clicking its image from products page +let showDescription = (id) => { + product = products[id - 1] + console.log("product is "+product.id) + let tenOptions = "" + let descStr = "" + descStr += `
+

${product.name}

+

Category: ${product.category}

+
+ +
+

${product.description}

+
+

Price: ${product.price}

+ +





+

This product is rated ${product.rating} based on ${product.reviews.length} review(s).

+

Reviews:

` + for (let i = 0; i < product.reviews.length; i++) { + descStr += `

${product.reviews[i].rating} - ${product.reviews[i].description}

` + } + currentDiv.innerHTML = descStr +} + +//define function to reset category and search on clicking title +let resetCat = () => { + document.getElementById('catSelect').value = "" + document.getElementById('searchbox').value = "" +} + +//define functions to detect and record instances of activity +let activityDetected = () => { + isActive = true +} +let checkActivity = () => { + if (isActive === true) { + isActive = false + } else if (isActive === false) { + alert("Are you still there?") + isActive = true + } +} + +let currentDiv = document.getElementById("currentPage") +//reload products on page when category selection is changed +document.getElementById('catSelect').addEventListener('change', loadPage); +//check if user has interacted with page in last minute +let isActive = true +var intervalID = window.setInterval(checkActivity, 60000) \ No newline at end of file diff --git a/public/products.js b/public/products.js index 4f07bb51..370a42c4 100644 --- a/public/products.js +++ b/public/products.js @@ -1,221 +1,34 @@ -const products = [{ - "_id": 1, - "name": "Body Luxuries Sweet Lavender Hand Sanitizer", - "description": "Makes your hands clean", - "reviews": 46, - "rating": 2, - "imgUrl": "http://dummyimage.com/136x167.bmp/cc0000/ffffff", - "price": "$95.11", - "category": "food", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - }, { - "description": "incubate strategic e-tailers", - "rating": 5 - }, { - "description": "transition synergistic partnerships", - "rating": 1 - }, { - "description": "matrix dynamic web-readiness", - "rating": 1 - }, { - "description": "exploit impactful platforms", - "rating": 4 - }, { - "description": "repurpose mission-critical schemas", - "rating": 1 - }, { - "description": "iterate open-source interfaces", - "rating": 3 - }, { - "description": "repurpose impactful interfaces", - "rating": 1 - }] - }, { - "_id": 2, - "name": "Topiramate", - "description": "A wonderful medicine that makes everything all better", - "reviews": 2, - "rating": 2, - "imgUrl": "http://dummyimage.com/125x134.jpg/cc0000/ffffff", - "price": "$37.09", - "category": "food", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - }, { - "description": "incubate strategic e-tailers", - "rating": 5 - }, { - "description": "transition synergistic partnerships", - "rating": 1 - } - ] - }, { - "_id": 3, - "name": "Almond", - "description": "A great treat that tastes great", - "reviews": 27, - "rating": 5, - "imgUrl": "http://dummyimage.com/149x190.jpg/dddddd/000000", - "price": "$51.83", - "category": "food", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 4, - "name": "VYTORIN", - "description": "Orchard as the place of occurrence of the external cause", - "reviews": 60, - "rating": 3, - "imgUrl": "http://dummyimage.com/162x153.jpg/cc0000/ffffff", - "price": "$86.93", - "category": "electronics", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 5, - "name": "Decolorized Iodine", - "description": "Kills germs on contact", - "reviews": 20, - "rating": 1, - "imgUrl": "http://dummyimage.com/120x245.jpg/cc0000/ffffff", - "price": "$70.10", - "category": "electronics", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 6, - "name": "Fresh Sugar Honey Tinted Lip Treatment SPF15", - "description": "Fix those chapped lips. ", - "reviews": 79, - "rating": 3, - "imgUrl": "http://dummyimage.com/211x227.bmp/5fa2dd/ffffff", - "price": "$39.25", - "category": "electronics", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 7, - "name": "LBel", - "description": "2-Propanol", - "reviews": 76, - "rating": 3, - "imgUrl": "http://dummyimage.com/212x144.jpg/ff4444/ffffff", - "price": "$99.91", - "category": "sporting", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 8, - "name": "Cholestyramine", - "description": "Help reduce cholesteral in the system", - "reviews": 74, - "rating": 3, - "imgUrl": "http://dummyimage.com/204x175.jpg/5fa2dd/ffffff", - "price": "$67.17", - "category": "sporting", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 9, - "name": "Risperidone", - "description": "cephalospor/oth beta-lactm antibiot, undet, sequela", - "reviews": 9, - "rating": 1, - "imgUrl": "http://dummyimage.com/212x108.bmp/cc0000/ffffff", - "price": "$96.84", - "category": "sporting", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - }, { - "description": "orchestrate dynamic schemas", - "rating": 2 - }, { - "description": "aggregate integrated convergence", - "rating": 4 - } - ] - }, { - "_id": 10, - "name": "MAC", - "description": "Other Gram-negative sepsis", - "reviews": 45, - "rating": 2, - "imgUrl": "http://dummyimage.com/189x109.png/cc0000/ffffff", - "price": "$74.37", - "category": "sporting", - "reviews": [{ - "description": "architect revolutionary deliverables", - "rating": 2 - } - ] - }]; +let products = [] + +let createProduct = () => { + let newStr = "" + newStr += ` +
+ Name:
+ Description:
+ Price:
+ +
+ ` + currentDiv.innerHTML = newStr +} + +let postProduct = () => { + let addName = document.getElementById("addName").value + let addDescription = document.getElementById("addDescription").value + let addPrice = document.getElementById("addPrice").value + console.log("name, desc, and price are: "+addName+addDescription+addPrice) + fetch("https://acastore.herokuapp.com/products", { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({name: addName, description: addDescription, price: addPrice, imgUrl: "https://via.placeholder.com/140x100", rating: 0, category: "", reviews: []}) + }).then((response) => response.json()).then(() => {refreshProducts()}) +} +// get products from server and then call loadPage +let refreshProducts = () => { + fetch("https://acastore.herokuapp.com/products").then((res) => {return res.json()}).then((data) => {products = data; loadPage()}) +} +refreshProducts() \ No newline at end of file diff --git a/public/shopCart.js b/public/shopCart.js new file mode 100644 index 00000000..40f773a6 --- /dev/null +++ b/public/shopCart.js @@ -0,0 +1,170 @@ +//check if current user has a cartId; if null, post a new cart and link it to the user +let initCart = () => { + let tempID = JSON.parse(localStorage.getItem("user")).id + if (JSON.parse(localStorage.getItem("user")).cartId === null){ + fetch("https://acastore.herokuapp.com/carts", { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({userId: tempID, products: [], cartValue: "0"}) + }) + .then((response) => response.json()) + //use PATCH method to add newly posted cart's ID to corresponding user + .then( (cartobj) => { + fetch(`https://acastore.herokuapp.com/users/${tempID}`, { + method: 'PATCH', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({cartId: cartobj.id}) + }) + .then((response) => response.json()) + .then((data) => {localStorage.setItem('user', JSON.stringify(data))}) + .then(() => {loadPage()}) + }) + } +} + +//define function to remove item from cart and subtract its value from the current total +let removeItem = (i) => { + let tempCartObj = {} + cart = [] + let cartValue = 0 + let itemValue = 0 + let tempStr = "" + let tempID = JSON.parse(localStorage.getItem("user")).cartId + fetch(`https://acastore.herokuapp.com/carts/${tempID}`).then((response) => {return response.json()}) + //assign data from server to a temporary cart Obj, and grab the current value and list of products + .then((data)=>{tempCartObj = data; cart = data.products; cartValue = Number(data.cartValue)}) + //using retrieved data, update cart object array and current cart value + .then(() => { + for (let j = cart[i].length - 1; j > 0; j --) { + if (cart[i][j] === "$") { + tempStr = cart[i].substr(j + 1) + } + } + itemValue = Number(tempStr) + cartValue -= itemValue + cart.splice(i, 1) + tempCartObj.products = cart + tempCartObj.cartValue = cartValue + }) + //use PUT method to update info on server + .then(() => { + fetch(`https://acastore.herokuapp.com/carts/${tempID}`, { + method: 'PUT', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(tempCartObj) + }) + }) + //after updating cart, refresh the cart page + .then(() => { + //add 100ms delay to ensure server has updated + setTimeout(showCart, 100) + }) +} + +//define function to add a product to cart based on its passed in ID, and add its value to the cart total +let addToCart = (id) => { + //init relevant variables, and begin with a GET request to the user's cart + let tempCartObj = {} + cart = [] + let cartValue = 0 + let quantity = document.getElementById("quantity" + id) + let tempID = JSON.parse(localStorage.getItem("user")).cartId + fetch(`https://acastore.herokuapp.com/carts/${tempID}`).then((response) => {return response.json()}) + //assign data from server to a temporary cart Obj, and grab the current value and list of products + .then((data)=>{tempCartObj = data; cart = data.products; cartValue = Number(data.cartValue)}) + //push the product, its quantity, and relevant information to the list, then assign that to the temporary object + .then(() => { + cart.push("" + quantity.value + "x " + products[id - 1].name + " - $" + (Number(products[id - 1].price.slice(1)) * Number(quantity.value)).toFixed(2)); + tempCartObj.products = cart}) + //derive the price of new product(s) and add them to the total cart value + .then(()=>{ + cartValue += (Number(products[id - 1].price.substring(1)) * Number(quantity.value)); + tempCartObj.cartValue = cartValue; + }) + //use PUT method to update cart object in server + .then(() => { + fetch(`https://acastore.herokuapp.com/carts/${tempID}`, { + method: 'PUT', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(tempCartObj) + }) + }) + //update status bar + let statusbar = document.getElementById("statusbar") + statusbar.innerHTML = `${products[id - 1].name} added to cart!` +} + +//define function to show each item in the cart and the total value +let showCart = () => { + //init relevant varibles + let tempID = JSON.parse(localStorage.getItem("user")).cartId + let cart = [] + let cartValue = 0 + let cartStr = "" + fetch(`https://acastore.herokuapp.com/carts/${tempID}`).then((response) => {return response.json()}) + //GET data from server to grab array of products and value from cart + .then((data)=>{cart = data.products; cartValue = Number(data.cartValue)}) + //display cart info to page + .then(() => { + if (!cart[0]) { + cartStr += `

Cart is Empty!

` + } else { + for (let i = 0; i < cart.length; i++) { + cartStr += `

${cart[i]}

` + } + } + cartStr += `

Total price: $${cartValue.toFixed([2])}



` + currentDiv.innerHTML = cartStr + }) + //empty status bar + let statusbar = document.getElementById("statusbar") + statusbar.innerHTML = " " +} + +//define a function to show checkout screen +let checkout = () => { + let checkoutStr = "" + let cart = [] + let cartValue = 0 + let tempID = JSON.parse(localStorage.getItem("user")).cartId + fetch(`https://acastore.herokuapp.com/carts/${tempID}`).then((response) => {return response.json()}) + //assign data from server to a temporary cart Obj, and grab the current value and list of products + .then((data)=>{cart = data.products; cartValue = Number(data.cartValue)}) + //if cart is empty, alert with an error + .then(() => { + if (cart.length === 1 && cart[0] === "") { + alert("Error! Empty cart.") + return + } + //otherwise, create a submit form using EmailJS API + checkoutStr += `
+ + + + + + +

+

Total price: $${cartValue.toFixed([2])}


+ +
` + currentDiv.innerHTML = checkoutStr + }) + //empty status bar + let statusbar = document.getElementById("statusbar") + statusbar.innerHTML = " " +} \ No newline at end of file diff --git a/public/signUp.js b/public/signUp.js new file mode 100644 index 00000000..9e233f8e --- /dev/null +++ b/public/signUp.js @@ -0,0 +1,21 @@ +//show sign up screen +let signUp = () => { + let descStr = "" + descStr += `
+ E-mail Address:
+ Password:
+ +
` + currentDiv.innerHTML += descStr +} +let submitSignUp = () => { + fetch("https://acastore.herokuapp.com/users", { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({email: document.getElementById("email").value, password: document.getElementById("password").value, cartId: null}) + }).then((response) => response.json()).then((data) => {localStorage.setItem('user', JSON.stringify(data))}).then(() => {loadPage()}) + +} \ No newline at end of file diff --git a/public/submitOrder.js b/public/submitOrder.js new file mode 100644 index 00000000..6ad313f5 --- /dev/null +++ b/public/submitOrder.js @@ -0,0 +1,26 @@ +//define a function to POST an order to the server using the current user information and cart +let submitOrder = () => { + let checkOutCart = [] + let checkOutValue = 0 + //retrieve current user's ID and e-mail + let userID = JSON.parse(localStorage.getItem("user")).id + let userEmail = JSON.parse(localStorage.getItem("user")).email + //retrieve current user's cart array + let tempID = JSON.parse(localStorage.getItem("user")).cartId + fetch(`https://acastore.herokuapp.com/carts/${tempID}`).then((response) => {return response.json()}) + //assign data from server to a temporary cart Obj, and grab the current value and list of products + .then((data)=>{checkOutCart = data.products; checkOutValue = Number(data.cartValue)}) + .then(() => { + fetch("https://acastore.herokuapp.com/orders", { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({userId: userID, items: checkOutCart}) + }) + }) + .then(() => { + alert("Order placed! Check your e-mail for verification.") + }) +} \ No newline at end of file