diff --git a/package.json b/package.json index 86646645b..f24a64c6c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "lint": "eslint *.js src/**/*.js", "format": "prettier --ignore-path ./.gitignore --write \"./**/*.{html,json,js,ts,css}\"", "test": "jest" - }, "author": "Shane Thompson", "license": "ISC", diff --git a/src/cart/index.html b/src/cart/index.html index f64b6f0c9..51339240b 100644 --- a/src/cart/index.html +++ b/src/cart/index.html @@ -1,98 +1,25 @@ - - + - - Sleep Outside | Cart - - + - -
- - -
- - - - - - - - - - - - - - - - - -
+ -

My Cart

- -
    - +
+
+
- - + diff --git a/src/checkout/index.html b/src/checkout/index.html index 876458650..a68e7fe81 100644 --- a/src/checkout/index.html +++ b/src/checkout/index.html @@ -1,88 +1,72 @@ - - + - - - - Sleep Outside | Checkout - - - + + - - -
- - - - - + - -
- -
- -

Review & Place your Order

- -
- +
+

Review & Place your Order

+
+
+
+ Shipping +
+ + + + +
+
+ + + + + + + + +
+
+
+ Payment + + + + + + +
+ +
+ Summary +
    +
  • + +

    +
  • +
  • + +

    +
  • +
  • + +

    +
  • +
  • + +

    +
  • +
+
+
+
- -
©NOT a real business
- +
- - diff --git a/src/css/style.css b/src/css/style.css index dee10f627..1d8031f48 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -110,6 +110,23 @@ button { fill: gray; } +.cart svg.pop { + animation: popAnimation 0.3s ease-in-out forwards; +} + +@keyframes popAnimation { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.2); + } + 100% { + transform: scale(1); + } +} + + /* End cart icon styles */ .mission { @@ -201,7 +218,31 @@ button { grid-row: 2; grid-column: 3; } +.product-msrp__price{ + font-size: small; + text-decoration: line-through; + color: var(--tertiary-color); +} +.hide { + display: none; +} + +#checkout { + margin-bottom: 10px; +} + +.alert { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5em; + background-color: var(--primary-color); + border: 1px solid darkorange; +} +.alert > p { + margin: 0; +} @media screen and (min-width: 500px) { body { max-width: 1080px; @@ -220,3 +261,125 @@ footer { font-size: var(--small-font); padding: 1em; } + +.categories-list{ + display: flex; + flex-direction: row; + justify-content: space-evenly; + flex-wrap: wrap; +} +.categories-list li{ + list-style-type: none; + text-align: center; +} +.categories-list img{ + width: 200px; +} +section{ + padding: 9px; +} + +header .cart{ + position: relative; +} + +.checkout { + padding: 0.5em; +} + +.checkout label { + display: block; +} + +.checkout input { + width: 100%; + font-size: 1.2em; +} + +.summary > ul { + padding-left: 0; + list-style-type: none; +} +.summary li { + display: flex; + justify-content: space-between; + align-items: center; +} +.summary input { + width: 25%; + font-size: 1em; +} + +.superscript{ + position:absolute; + top: 5px; + right: -5px; + font-size: x-small; + background-color: #ffffff; + border: 2px solid var(--primary-color); + border-radius: 50%; + padding: 3px 5px; +} +.background-darkblue{ + background-color: #333399; +} +.foreground-white{ + color: #ffffff; +} +.title { + text-transform: capitalize; +} + +.headMenu div{ + display:inline-block; + vertical-align: middle; +} +.search { + margin-right: 16px; +} +.search svg { + padding-top: 6px; + vertical-align: bottom; +} +.headMenu .search input{ + margin-top: 20px; +} +/* Carousel */ +.myImage { + display: none; +} +/* Next and Previous buttons */ +.previous, .next { + cursor: pointer; + position: absolute; + top: 50%; + width: auto; + margin-top: -10px; + padding: 16px; + color: black; + font-weight: bold; + font-size: 18px; + transition: 0.6s ease; + border-radius: 0 3px 3px 0; + user-select: none; +} + +.next { + right: 25%; + border-radius: 3px 0 0 3px; +} + +.previous { + left: 25%; +} + +.numTest { + font-size: 14px; + padding: 8px 12px; + text-align: center; +} + +.previous:hover, .next:hover { + background-color: rgba(0,0,0,0.8); + color: white; +} \ No newline at end of file diff --git a/src/index.html b/src/index.html index 35ac63ce6..ad8a27b8f 100644 --- a/src/index.html +++ b/src/index.html @@ -5,31 +5,16 @@ Sleep Outside | Home + + + -
- - +
+
-
-

Top Products

-
-
©NOT a real business
+
diff --git a/src/js/Alert.js b/src/js/Alert.js new file mode 100644 index 000000000..7d554986f --- /dev/null +++ b/src/js/Alert.js @@ -0,0 +1,40 @@ +import {convertToJson} from './utils.mjs'; + +function alertTemplate(alert) { + return ` +

${alert.message}

+ `; +} + +//Alert class +export default class Alert { + constructor(message) { + this.message = message; + this.path = `../json/alerts.json`; + this.renderAlerts(); + } + async getData() { // Mark as async + return fetch(this.path) + .then(convertToJson) + .then((data) => data); + } + async getAlertsHtml() { // Mark as async + let alerts = await this.getData(); + let alertsHTML = ''; + if(alerts){ + + alerts.forEach((alert) => { + alertsHTML += alertTemplate(alert); + }); + } + return alertsHTML; + } + async renderAlerts(){ // Mark as async + const element = document.querySelector('.alert-list'); + const alertsHtml = await this.getAlertsHtml(); + element.insertAdjacentHTML( + 'afterBegin', + alertsHtml + ); + } +} diff --git a/src/js/CheckoutProcess.mjs b/src/js/CheckoutProcess.mjs new file mode 100644 index 000000000..8dab4d5e4 --- /dev/null +++ b/src/js/CheckoutProcess.mjs @@ -0,0 +1,108 @@ +import { getLocalStorage, alertMessage, removeAllAlerts } from "./utils.mjs"; +import ExternalServices from "./ExternalServices.mjs"; + +const service = new ExternalServices(); + +function formDataToJSON(formElement) { + const formData = new FormData(formElement); + const convertedJSON = {}; + + formData.forEach(function (value, key) { + convertedJSON[key] = value; + }); + + return convertedJSON; +} + +function logItems(items) { + const simpleItems = items.map((item) => { + return { + id: item.Id, + price: item.FinalPrice, + name: item.Name, + quantity: 1, + }; + }); + return simpleItems; +} + +export default class CheckoutProcess { + constructor(key, outputSelector) { + this.key = key; + this.outputSelector = outputSelector; + this.list = []; + this.itemTotal = 0; + this.shipping = 0; + this.tax = 0; + this.orderTotal = 0; + } + + init() { + this.list = getLocalStorage(this.key); + this.calculateItemSummary(); + } + + calculateItemSummary() { + const summary = document.querySelector(this.outputSelector + " #cartTotal"); + const itemNum = document.querySelector(this.outputSelector + " #num-of-items"); + + itemNum.innerText = this.list.length; + + const amounts = this.list.map((item) => item.FinalPrice); + this.itemTotal = amounts.reduce((sum, item) => sum + item); + summary.innerText = "$" + this.itemTotal.toFixed(2); + } + + calculateOrderTotal() { + this.shipping = 10 + (this.list.length - 1) * 2; + this.tax = (this.itemTotal * 0.06).toFixed(2); + this.orderTotal = ( + parseFloat(this.itemTotal) + + parseFloat(this.shipping) + + parseFloat(this.tax) + ).toFixed(2); + this.displayOrderTotals(); + } + + displayOrderTotals() { + const shipping = document.querySelector(this.outputSelector + " #shipping"); + const tax = document.querySelector(this.outputSelector + " #tax"); + const orderTotal = document.querySelector(this.outputSelector + " #ontotal"); + + shipping.innerText = "$" + this.shipping; + tax.innerText = "$" + this.tax; + orderTotal.innerText = "$" + this.orderTotal; + } + + async checkout() { + const formElement = document.forms["checkout"]; + formElement.checkValidity(); + const json = formDataToJSON(formElement); + + json.orderDate = new Date(); + json.orderTotal = this.orderTotal; + json.tax = this.tax; + json.shipping = this.shipping; + json.items = logItems(this.list); + + console.log(json); + + try { + const res = await service.checkout(json); + console.log(res); + + if(res.orderId) { + document.querySelector('.h2-thankyou').textContent = `Thank You!`; + document.querySelector('.form-checkout').innerHTML = `
Your Order has successfully been placed. Order ID #${res.orderId}
`; + } + } catch (err) { + + removeAllAlerts(); + for (let message in err.message) { + alertMessage(err.message[message]); + } + console.log(err); + } + } + +} \ No newline at end of file diff --git a/src/js/ExternalServices.mjs b/src/js/ExternalServices.mjs new file mode 100644 index 000000000..ebd49faaa --- /dev/null +++ b/src/js/ExternalServices.mjs @@ -0,0 +1,34 @@ +const baseURL = import.meta.env.VITE_SERVER_URL; +async function convertToJson(res) { + const data = await res.json(); + if (res.ok) { + return data; + } else { + throw { name: "servicesError", message: data }; + } +} + +export default class ExternalServices { + constructor() {} + + async getDa(category) { + const response = await fetch(baseURL + `products/search/${category}`); + const data = await convertToJson(response); + return data.Result; + } + async findProductById(id) { + const response = await fetch(baseURL + `product/${id}`); + const data = await convertToJson(response); + return data.Result; + } + async checkout(payload) { + const options = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }; + return await fetch(baseURL + "checkout/", options).then(convertToJson); + } +} \ No newline at end of file diff --git a/src/js/ProductData.mjs b/src/js/ProductData.mjs index 2361ff218..cc7c586b4 100644 --- a/src/js/ProductData.mjs +++ b/src/js/ProductData.mjs @@ -1,3 +1,4 @@ +const baseURL = import.meta.env.VITE_SERVER_URL; function convertToJson(res) { if (res.ok) { return res.json(); @@ -7,17 +8,40 @@ function convertToJson(res) { } export default class ProductData { - constructor(category) { - this.category = category; - this.path = `../json/${this.category}.json`; + constructor() { + } - getData() { - return fetch(this.path) - .then(convertToJson) - .then((data) => data); + async getData(category) { + const response = await fetch(baseURL + `products/search/${category}`); + const data = await convertToJson(response); + return data.Result; + } + async getData(category, sort) { + const response = await fetch(baseURL + `products/search/${category}`); + const data = await convertToJson(response); + // const sorted = data.sort(this.productSort(data, sort)); + const sorted = this.productSort(data.Result, sort); + return sorted; } async findProductById(id) { - const products = await this.getData(); - return products.find((item) => item.Id === id); + const response = await fetch(`${baseURL}product/${id}`); + const data = await convertToJson(response); + return data.Result; + } + + productSort(data, selection) { + const nameSort = data; + if (selection == "name") { + nameSort.sort((a, b) => { + const nameA = a.Brand.Name; + const nameB = b.Brand.Name; + if (nameA < nameB) return -1; + if (nameA > nameB) return 1; + return 0; + }); + } else if (selection == "price") { + nameSort.sort((a, b) => a.FinalPrice - b.FinalPrice); + } + return nameSort; } } diff --git a/src/js/ProductDetails.mjs b/src/js/ProductDetails.mjs new file mode 100644 index 000000000..2eed3c8ad --- /dev/null +++ b/src/js/ProductDetails.mjs @@ -0,0 +1,101 @@ +import { setLocalStorage, getLocalStorage, updateCountItemsInCart } from "./utils.mjs"; +window.plusSlides = function(n) { + showSlides(slideIndex += n); +} +function productDetailsTemplate(product){ + return `
+

${product.Brand.Name}

+ +

${product.NameWithoutBrand}

+ + +

MSRP: $${product.SuggestedRetailPrice}

+

$${product.FinalPrice}

+ +

${product.Colors[0].ColorName}

+ +

+ ${product.DescriptionHtmlSimple} +

+ +
+ +
+
` + } + + function renderImages (product) { + const numImages = product.Images.ExtraImages; + let div = ''; + if (numImages != null) { + for (let i = 0; i < numImages.length; i++) { + const imageSelect = ` +
+
${i + 1}/${numImages.length}
+ ${numImages[i].Title} +
` + console.log(numImages); + div += imageSelect; + } + const arrowButtons = ` + + `; + div += arrowButtons; + } else { + return ` + + ${product.Name} +

${product.Brand.Name}

+

${product.NameWithoutBrand}

+

${product.FinalPrice}

+ + ` +} + +export default class ProductList{ + constructor(category, dataSource, listElement, sort) { + this.category = category; + this.sort = sort; + this.dataSource = dataSource; + this.listElement = listElement; + } + async init() { + const list = await this.dataSource.getData(this.category, this.sort); + //let filterList = this.filterProductList(list) + //this.renderList(filterList); + this.renderList(list); + document.querySelector('.title').innerHTML = this.category; + } + renderList(productList){ + renderListWithTemplate(productCardTemplate,this.listElement,productList); + + } + filterProductList(productList){ // A completely sucky way to filter + return productList.filter((item) => { + return ['880RR', '985RF', '985PR', '344YJ'].includes(item.Id); + }); + } + +} diff --git a/src/js/cart.js b/src/js/cart.js index a2fb3d8ef..b5c8619ea 100644 --- a/src/js/cart.js +++ b/src/js/cart.js @@ -1,16 +1,94 @@ -import { getLocalStorage } from "./utils.mjs"; - +import { getLocalStorage, setLocalStorage, showCountItemsInCart, updateCountItemsInCart} from './utils.mjs'; function renderCartContents() { - const cartItems = getLocalStorage("so-cart"); - const htmlItems = cartItems.map((item) => cartItemTemplate(item)); - document.querySelector(".product-list").innerHTML = htmlItems.join(""); + if (localStorage.getItem('so-cart') !== null) { + const cartItems = getLocalStorage('so-cart'); + const aggregateCartItems = aggregateCartItemsWithQuantity(cartItems); + const htmlItems = aggregateCartItems.map((item) => cartItemTemplate(item)); + const total = getCartTotal(cartItems); + const totalDiv = document.querySelector('.total'); + totalDiv.innerHTML = total === 0 ? '' : cartTotalTemplate(total); // correcting my adding to cart in a new branch + totalDiv.classList.toggle('hide', total === 0); + + document.querySelector('.product-list').innerHTML = htmlItems.join(''); + document.querySelectorAll('.remove-from-cart').forEach((item)=>{ + item.addEventListener('click', (event)=>{ + + removeFromCart(event.target.dataset.id); + }); + }) + document.querySelectorAll('.addQuantity').forEach((item)=>{ + item.addEventListener('click', (event)=>{ + let qtyElem = document.querySelector('#qty-' + event.target.dataset.id); + let qtyValue = parseInt(qtyElem.innerHTML); + qtyValue++; + qtyElem.innerHTML = qtyValue; + addItemToCart(event.target.dataset.id); + }); + }) + document.querySelectorAll('.subQuantity').forEach((item)=>{ + item.addEventListener('click', (event)=>{ + let qtyElem = document.querySelector('#qty-' + event.target.dataset.id); + let qtyValue = parseInt(qtyElem.innerHTML); + qtyValue--; + qtyElem.innerHTML = qtyValue; + removeQuantityFromCart(event.target.dataset.id); + }); + }) + + document.getElementById('checkout').addEventListener('click', function() { + + window.location.href = '/checkout/index.html'; + }); + } + + setupAddToCartAnimation(); +} + +function setupAddToCartAnimation() { + + const addQuantityButtons = document.querySelectorAll('.addQuantity'); + + addQuantityButtons.forEach(button => { + button.addEventListener('click', () => { + + animateAddToCart(); + }); + }); +} + +function animateAddToCart() { + + const svgElement = document.querySelector('.cart svg'); + svgElement.classList.add('pop'); + + setTimeout(() => { + svgElement.classList.remove('pop'); + }, 1000); +} + +function aggregateCartItemsWithQuantity(cartItems) { + let aggregateCartItems = []; + + cartItems.forEach((item) => { + + const i = aggregateCartItems.findIndex(aggregateItem => aggregateItem.Id == item.Id); + if(i !== -1) { + aggregateCartItems[i].quantity++; + } else { + item.quantity = 1; + aggregateCartItems.push(item); + } + }); + + return aggregateCartItems; } function cartItemTemplate(item) { const newItem = `
  • + X ${item.Name} @@ -18,11 +96,62 @@ function cartItemTemplate(item) {

    ${item.Name}

    ${item.Colors[0].ColorName}

    -

    qty: 1

    +

    Quantity: ${item.quantity} + -

    $${item.FinalPrice}

  • `; return newItem; } -renderCartContents(); +function getCartTotal(cartItems) { + + let total = 0; + cartItems.forEach((item)=>{ + total += item.FinalPrice; + }) + return total; +} + +function cartTotalTemplate(total) { + return `
    Your total is going to be: $${total.toFixed(2)}
    ` +} + +function removeFromCart(id){ + const cartItems = getLocalStorage('so-cart'); + for (let i = cartItems.length - 1; i >= 0; i--) { + if (cartItems[i].Id === id) { + cartItems.splice(i, 1); + animateAddToCart(); + } + } + + setLocalStorage('so-cart',cartItems); + renderCartContents(); + updateCountItemsInCart() +} + +function removeQuantityFromCart(id){ + const cartItems = getLocalStorage('so-cart'); + for (let i = cartItems.length - 1; i >= 0; i--) { + if (cartItems[i].Id === id) { + cartItems.splice(i, 1); + animateAddToCart(); + break; + } + } + + setLocalStorage('so-cart',cartItems); + renderCartContents(); + updateCountItemsInCart() +} + +function addItemToCart(Id) { + let cart = getLocalStorage('so-cart') || []; + let item = cart.find(i => i.Id == Id) + cart.push(item); + setLocalStorage('so-cart', cart); + renderCartContents(); + updateCountItemsInCart() +} + +renderCartContents(); \ No newline at end of file diff --git a/src/js/checkout.js b/src/js/checkout.js new file mode 100644 index 000000000..7d6f9f485 --- /dev/null +++ b/src/js/checkout.js @@ -0,0 +1,22 @@ +import { loadHeaderFooter, alertMessage } from "./utils.mjs"; +import CheckoutProcess from "./CheckoutProcess.mjs"; + +loadHeaderFooter(); + +const myCheckout = new CheckoutProcess("so-cart", ".checkout"); +myCheckout.init(); + +document + .querySelector("#zipn") + .addEventListener("blur", myCheckout.calculateOrderTotal.bind(myCheckout)); + +document.querySelector("#checkSubmit").addEventListener("click", async (e) => { + e.preventDefault(); + myCheckout.checkout(); +}); + +document.forms['checkout'] +.addEventListener('submit', (e) => { + e.preventDefault(); + myCheckout.checkout(); +}); \ No newline at end of file diff --git a/src/js/global.js b/src/js/global.js new file mode 100644 index 000000000..edc0f24e6 --- /dev/null +++ b/src/js/global.js @@ -0,0 +1,12 @@ +import {showCountItemsInCart, loadHeaderFooter} from './utils.mjs'; +//Things that happen on every page in the site +loadHeaderFooter(() => { + //Things that have to happen AFTER the header and footer load + showCountItemsInCart(); + document.querySelector('.search form').addEventListener('submit', function(event) { + event.preventDefault(); + var searchQuery = this.querySelector('input[type="text"]').value; + window.location.href = '/product-listing/index.html?search=' + searchQuery; + }); +}); +// \ No newline at end of file diff --git a/src/js/main.js b/src/js/main.js index e69de29bb..1097700b6 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -0,0 +1,13 @@ +import Alert from './Alert'; +import ProductData from './ProductData.mjs'; +import ProductList from './ProductList.mjs'; + + + +const alert = new Alert(); +const dataSource = new ProductData('tents'); +const element = document.querySelector('.product-list'); +const productList = new ProductList('tents', dataSource, element); + +//productList.init(); + diff --git a/src/js/product-listing.js b/src/js/product-listing.js new file mode 100644 index 000000000..e9d99b15f --- /dev/null +++ b/src/js/product-listing.js @@ -0,0 +1,29 @@ +import ProductData from './ProductData.mjs'; +import ProductList from './ProductList.mjs'; +import {getParam} from './utils.mjs' +const category = getParam('category'); + +const search = getParam('search'); +const selectElement = document.querySelector('.sProd'); +let sort = selectElement.value; +let dataSource = new ProductData(); +let element = document.querySelector('.product-list'); +let productList; +if(search){ + productList = new ProductList(search, dataSource, element); +}else if(category){ + productList = new ProductList(category, dataSource, element); +} + +productList.init(); + +document.querySelector('.sProd').addEventListener('change', (event) => { + // console.log(event.target.value); + sort = event.target.value; + dataSource = new ProductData(); + element = document.querySelector('.product-list'); + productList = new ProductList(category, dataSource, element, sort); + document.querySelector('.product-list').innerHTML = ''; + productList.init(); +}); + diff --git a/src/js/product.js b/src/js/product.js index 0b8d0aa67..f55f7c77c 100644 --- a/src/js/product.js +++ b/src/js/product.js @@ -1,18 +1,10 @@ -import { setLocalStorage } from "./utils.mjs"; -import ProductData from "./ProductData.mjs"; +import { getParam, showCountItemsInCart} from './utils.mjs'; +import ProductData from './ProductData.mjs'; +import ProductDetails from './ProductDetails.mjs'; -const dataSource = new ProductData("tents"); +const dataSource = new ProductData('tents'); -function addProductToCart(product) { - setLocalStorage("so-cart", product); -} -// add to cart button event handler -async function addToCartHandler(e) { - const product = await dataSource.findProductById(e.target.dataset.id); - addProductToCart(product); -} - -// add listener to Add to Cart button -document - .getElementById("addToCart") - .addEventListener("click", addToCartHandler); +const productId = getParam('product'); +const product = new ProductDetails(productId, dataSource); +product.init(); +showCountItemsInCart(); \ No newline at end of file diff --git a/src/js/utils.mjs b/src/js/utils.mjs index 1a04d87ff..cb3ce6d53 100644 --- a/src/js/utils.mjs +++ b/src/js/utils.mjs @@ -21,3 +21,101 @@ export function setClick(selector, callback) { }); qs(selector).addEventListener("click", callback); } +export function getParam(param){ + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + return urlParams.get(param); +} + +export function renderListWithTemplate(templateFn, parentElement, list, position='afterbegin', clear=false){ + const htmlItems = list.map(templateFn); + if(clear) parentElement.clear(); + parentElement.insertAdjacentHTML(position, htmlItems.join("")); +} + +export function renderWithTemplate(template, parentElement, data = null, callback = null){ + //const html = templateFn(data); + //if(clear) parentElement.clear(); + parentElement.insertAdjacentHTML('afterbegin', template); + + //literally zero clue why this is being done, also data is never actually used in this function, so why pass it with the template???? + if(callback) { + callback(data); + } +} + +export function showCountItemsInCart(){ + let cart = getLocalStorage('so-cart'); + if(cart){ + if(cart.length > 0){ + const html = cartCountTemplate(cart.length); + const cartElement = document.querySelector('header .cart'); + cartElement.insertAdjacentHTML('afterbegin', html); + } + } +} +export function updateCountItemsInCart(){ + const cartItems = getLocalStorage('so-cart'); + let cartCount = cartItems.length; + let cartCountElement = document.querySelector('header .cart .superscript'); + if(cartCountElement){ + cartCountElement.innerHTML = cartCount; + }else{ + let html = cartCountTemplate(cartCount); + let element = document.querySelector('header .cart'); + element.insertAdjacentHTML('afterbegin', html); + } +} +function cartCountTemplate(cartCount){ + return `
    ${cartCount}
    `; +} + +export function convertToJson(res) { + if (res.ok) { + return res.json(); + } else { + throw new Error("Bad Response"); + } +} +export async function loadHeaderFooter(callback){ + const headerTemplate = await loadTemplate('/partials/header.html'); + const headerElem = document.getElementById("header"); + + const footerTemplate = await loadTemplate('/partials/footer.html'); + const footerElem = document.getElementById("footer"); + + renderWithTemplate(headerTemplate, headerElem); + renderWithTemplate(footerTemplate, footerElem); + if(callback){ + callback(); + } + +} + +async function loadTemplate(path) { + const result = await fetch(path); + const template = await result.text(); + return template; +} + +export function alertMessage(message, scroll = true, duration = 3000) { + const alert = document.createElement("div"); + alert.classList.add("alert"); + alert.innerHTML = `

    ${message}

    X`; + + alert.addEventListener("click", function (e) { + if (e.target.tagName == "SPAN") { + main.removeChild(this); + } + }); + const main = document.querySelector("main"); + main.prepend(alert); + + if (scroll) window.scrollTo(0, 0); + +} + +export function removeAllAlerts() { + const alerts = document.querySelectorAll(".alert"); + alerts.forEach((alert) => alert.parentNode.removeChild(alert)); +} \ No newline at end of file diff --git a/src/product-listing/index.html b/src/product-listing/index.html new file mode 100644 index 000000000..29c2db972 --- /dev/null +++ b/src/product-listing/index.html @@ -0,0 +1,31 @@ + + + + + + Sleep Outside | Home + + + + + + +
    +
    +

    Top Products:

    +
    + + +
    +
    +
      +
    +
    +
    +
    + + diff --git a/src/product_pages/cedar-ridge-rimrock-2.html b/src/product_pages/cedar-ridge-rimrock-2.html deleted file mode 100644 index c05810379..000000000 --- a/src/product_pages/cedar-ridge-rimrock-2.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - Sleep Outside | Cedar Ridge Rimrock 2-person tent - - - - - - - -
    - - - -
    - -
    -
    -

    Cedar Ridge

    - -

    Rimrock Tent - 2-Person, 3-Season

    - - Rimrock Tent - 2-Person, 3-Season - -

    $69.99

    - -

    Rust/Clay

    - -

    - Lightweight and ready for adventure, this Cedar Ridge Rimrock tent - boasts a weather-ready design that includes a tub-style floor and - factory-sealed rain fly -

    - -
    - -
    -
    -
    - -
    ©NOT a real business
    - - diff --git a/src/product_pages/index.html b/src/product_pages/index.html new file mode 100644 index 000000000..a2df3913b --- /dev/null +++ b/src/product_pages/index.html @@ -0,0 +1,18 @@ + + + + + + Sleep Outside | Cedar Ridge Rimrock 2-person tent + + + + + + +
    +
    +
    + + diff --git a/src/product_pages/marmot-ajax-3.html b/src/product_pages/marmot-ajax-3.html deleted file mode 100644 index 936f67541..000000000 --- a/src/product_pages/marmot-ajax-3.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - Sleep Outside | Marmot Ajax 3 person tent - - - - - - - -
    - - - -
    - -
    -
    -

    Marmot

    - -

    Ajax Tent - 3-Person, 3-Season

    - - Marmot Ajax tent - -

    $199.99

    - -

    Pale Pumpkin/Terracotta

    - -

    - Get out and enjoy nature with Marmot's Ajax tent, featuring a - smart design with durable, waterproof construction and two doors for - easy access. -

    - -
    - -
    -
    -
    - -
    ©NOT a real business
    - - diff --git a/src/product_pages/northface-alpine-3.html b/src/product_pages/northface-alpine-3.html deleted file mode 100644 index 8d0ab07e3..000000000 --- a/src/product_pages/northface-alpine-3.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - Sleep Outside | North Face Alpine Guide 3-person tent - - - - - - - -
    - - - -
    - -
    -
    -

    The North Face

    - -

    Alpine Guide Tent - 3-Person, 4-Season

    - - Alpine Guide Tent - 3-Person, 4-Season - -

    $349.99

    - -

    Canary Yellow/High Rise Grey

    - -

    - Be ready for any outdoor adventure in low elevations and high-alpine - environments alike with the hybrid design of The North Face's - Alpine Guide four-season tent. It is made from durable, waterproof - Featherlite NSL pole system and an easy to pitch design. -

    - -
    - -
    -
    -
    - -
    ©NOT a real business
    - - diff --git a/src/product_pages/northface-talus-4.html b/src/product_pages/northface-talus-4.html deleted file mode 100644 index 891c22612..000000000 --- a/src/product_pages/northface-talus-4.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - Sleep Outside | North Face Talus 4-person tent - - - - - - - -
    - - - -
    - -
    -
    -

    The North Face

    - -

    Talus Tent - 4-Person, 3-Season

    - - Talus Tent - 4-Person, 3-Season - -

    $199.99

    - -

    Golden Oak/Saffron Yellow

    - -

    - Enjoy a fun night under stars with your favorite people in The North - Face's Talus four-person tent, featuring durable construction with - a roomy interior, an advanced DAC Featherlite NSL pole system and an - easy to pitch design. -

    - -
    - -
    -
    -
    - -
    ©NOT a real business
    - - diff --git a/src/images/banner-sm.jpg b/src/public/images/banner-sm.jpg similarity index 100% rename from src/images/banner-sm.jpg rename to src/public/images/banner-sm.jpg diff --git a/src/images/banner.jpg b/src/public/images/banner.jpg similarity index 100% rename from src/images/banner.jpg rename to src/public/images/banner.jpg diff --git a/src/images/logos/marmot-160x100.jpg b/src/public/images/logos/marmot-160x100.jpg similarity index 100% rename from src/images/logos/marmot-160x100.jpg rename to src/public/images/logos/marmot-160x100.jpg diff --git a/src/images/noun_Backpack_2389275.svg b/src/public/images/noun_Backpack_2389275.svg similarity index 100% rename from src/images/noun_Backpack_2389275.svg rename to src/public/images/noun_Backpack_2389275.svg diff --git a/src/images/noun_Backpack_65884.svg b/src/public/images/noun_Backpack_65884.svg similarity index 100% rename from src/images/noun_Backpack_65884.svg rename to src/public/images/noun_Backpack_65884.svg diff --git a/src/public/images/noun_Hammock_791143.svg b/src/public/images/noun_Hammock_791143.svg new file mode 100644 index 000000000..3601429bf --- /dev/null +++ b/src/public/images/noun_Hammock_791143.svg @@ -0,0 +1,3 @@ +Artboard Copy 2Created with Sketch. + + diff --git a/src/public/images/noun_Sleeping Bag_3544775.svg b/src/public/images/noun_Sleeping Bag_3544775.svg new file mode 100644 index 000000000..6ab95a6bd --- /dev/null +++ b/src/public/images/noun_Sleeping Bag_3544775.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/noun_Tent_2517.svg b/src/public/images/noun_Tent_2517.svg similarity index 99% rename from src/images/noun_Tent_2517.svg rename to src/public/images/noun_Tent_2517.svg index 66edad36a..633bf17ab 100644 --- a/src/images/noun_Tent_2517.svg +++ b/src/public/images/noun_Tent_2517.svg @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/src/public/images/noun_Tent_3544767.svg b/src/public/images/noun_Tent_3544767.svg new file mode 100644 index 000000000..f437f0f9f --- /dev/null +++ b/src/public/images/noun_Tent_3544767.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/public/images/noun_carrier bag_3544771.svg b/src/public/images/noun_carrier bag_3544771.svg new file mode 100644 index 000000000..b95f0e0fd --- /dev/null +++ b/src/public/images/noun_carrier bag_3544771.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/tents/cedar-ridge-rimrock-tent-2-person-3-season-in-rust-clay~p~344yj_01~320.jpg b/src/public/images/tents/cedar-ridge-rimrock-tent-2-person-3-season-in-rust-clay~p~344yj_01~320.jpg similarity index 100% rename from src/images/tents/cedar-ridge-rimrock-tent-2-person-3-season-in-rust-clay~p~344yj_01~320.jpg rename to src/public/images/tents/cedar-ridge-rimrock-tent-2-person-3-season-in-rust-clay~p~344yj_01~320.jpg diff --git a/src/images/tents/marmot-ajax-tent-3-person-3-season-in-pale-pumpkin-terracotta~p~880rr_01~320.jpg b/src/public/images/tents/marmot-ajax-tent-3-person-3-season-in-pale-pumpkin-terracotta~p~880rr_01~320.jpg similarity index 100% rename from src/images/tents/marmot-ajax-tent-3-person-3-season-in-pale-pumpkin-terracotta~p~880rr_01~320.jpg rename to src/public/images/tents/marmot-ajax-tent-3-person-3-season-in-pale-pumpkin-terracotta~p~880rr_01~320.jpg diff --git a/src/images/tents/the-north-face-alpine-guide-tent-3-person-4-season-in-canary-yellow-high-rise-grey~p~985pr_01~320.jpg b/src/public/images/tents/the-north-face-alpine-guide-tent-3-person-4-season-in-canary-yellow-high-rise-grey~p~985pr_01~320.jpg similarity index 100% rename from src/images/tents/the-north-face-alpine-guide-tent-3-person-4-season-in-canary-yellow-high-rise-grey~p~985pr_01~320.jpg rename to src/public/images/tents/the-north-face-alpine-guide-tent-3-person-4-season-in-canary-yellow-high-rise-grey~p~985pr_01~320.jpg diff --git a/src/images/tents/the-north-face-talus-tent-4-person-3-season-in-golden-oak-saffron-yellow~p~985rf_01~320.jpg b/src/public/images/tents/the-north-face-talus-tent-4-person-3-season-in-golden-oak-saffron-yellow~p~985rf_01~320.jpg similarity index 100% rename from src/images/tents/the-north-face-talus-tent-4-person-3-season-in-golden-oak-saffron-yellow~p~985rf_01~320.jpg rename to src/public/images/tents/the-north-face-talus-tent-4-person-3-season-in-golden-oak-saffron-yellow~p~985rf_01~320.jpg diff --git a/src/public/json/alerts.json b/src/public/json/alerts.json new file mode 100644 index 000000000..8921b866e --- /dev/null +++ b/src/public/json/alerts.json @@ -0,0 +1 @@ +[{"message": "This is only a test", "background": "darkblue", "color": "white"}] \ No newline at end of file diff --git a/src/json/backpacks.json b/src/public/json/backpacks.json similarity index 100% rename from src/json/backpacks.json rename to src/public/json/backpacks.json diff --git a/src/json/sleeping-bags.json b/src/public/json/sleeping-bags.json similarity index 99% rename from src/json/sleeping-bags.json rename to src/public/json/sleeping-bags.json index 6bf7a1243..5d015123c 100644 --- a/src/json/sleeping-bags.json +++ b/src/public/json/sleeping-bags.json @@ -35,9 +35,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -93,9 +91,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "LH" - ] + "ZIPPER": ["LH"] }, "Colors": [ { @@ -196,12 +192,8 @@ ] }, "SizesAvailable": { - "LENGTH": [ - "REG" - ], - "ZIPPER": [ - "R" - ] + "LENGTH": ["REG"], + "ZIPPER": ["R"] }, "Colors": [ { @@ -323,9 +315,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -370,9 +360,7 @@ "ExtraImages": null }, "SizesAvailable": { - "HAND": [ - "LH" - ] + "HAND": ["LH"] }, "Colors": [ { @@ -422,9 +410,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -474,9 +460,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -526,9 +510,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -578,9 +560,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -630,9 +610,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -730,9 +708,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -1018,9 +994,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "L" - ] + "ZIPPER": ["L"] }, "Colors": [ { @@ -1070,9 +1044,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "R" - ] + "ZIPPER": ["R"] }, "Colors": [ { @@ -1122,9 +1094,7 @@ ] }, "SizesAvailable": { - "ZIPPER": [ - "RH" - ] + "ZIPPER": ["RH"] }, "Colors": [ { diff --git a/src/json/tents.json b/src/public/json/tents.json similarity index 99% rename from src/json/tents.json rename to src/public/json/tents.json index 98029223e..d24bd8bcb 100644 --- a/src/json/tents.json +++ b/src/public/json/tents.json @@ -99,7 +99,7 @@ { "ColorCode": "01", "ColorName": "Pale Pumpkin/Terracotta" - } + } ], "DescriptionHtmlSimple": "Excess. Get out and enjoy nature with Marmot's Ajax tent, featuring a smart design with durable, waterproof construction and two doors for easy access.", "SuggestedRetailPrice": 275.0, diff --git a/src/public/partials/footer.html b/src/public/partials/footer.html new file mode 100644 index 000000000..495b91972 --- /dev/null +++ b/src/public/partials/footer.html @@ -0,0 +1 @@ +©NOT a real business \ No newline at end of file diff --git a/src/public/partials/header.html b/src/public/partials/header.html new file mode 100644 index 000000000..3640453b4 --- /dev/null +++ b/src/public/partials/header.html @@ -0,0 +1,28 @@ + + diff --git a/vite.config.js b/vite.config.js index 4a5eef384..bb79295e4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,29 +1,17 @@ -import { resolve } from "path"; -import { defineConfig } from "vite"; +import { resolve } from 'path'; +import { defineConfig } from 'vite'; export default defineConfig({ - root: "src/", - + root: 'src/', build: { - outDir: "../dist", + outDir: '../dist', rollupOptions: { input: { - main: resolve(__dirname, "src/index.html"), - cart: resolve(__dirname, "src/cart/index.html"), - checkout: resolve(__dirname, "src/checkout/index.html"), - product1: resolve( - __dirname, - "src/product_pages/cedar-ridge-rimrock-2.html" - ), - product2: resolve(__dirname, "src/product_pages/marmot-ajax-3.html"), - product3: resolve( - __dirname, - "src/product_pages/northface-alpine-3.html" - ), - product4: resolve( - __dirname, - "src/product_pages/northface-talus-4.html" - ), + main: resolve(__dirname, 'src/index.html'), + cart: resolve(__dirname, 'src/cart/index.html'), + checkout: resolve(__dirname, 'src/checkout/index.html'), + productListing: resolve(__dirname, 'src/product-listing/index.html'), + product: resolve(__dirname, 'src/product_pages/index.html'), }, }, },