Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
815 changes: 466 additions & 349 deletions package-lock.json

Large diffs are not rendered by default.

74 changes: 5 additions & 69 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sleep Outside | Home</title>
<link rel="stylesheet" href="/css/style.css" />
<script src="/js/main.js" type="module"></script>
</head>
<body>
<header class="divider">
<div class="logo">
<img src="/images/noun_Tent_2517.svg" alt="tent image for logo">
<a href="index.html"> Sleep<span class="highlight">Outside</span></a>
</div>
<div class="cart">
<a href="cart/index.html">

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M18.9 32.6c1.1 2.4 2.5 3.3 5.4 3.3 1.6 0 3.6-0.3 5.9-0.6 3.2-0.5 6.9-1 11.2-1 2.1 0 4.3 0.1 6.4 0.3 2.1 0.1 4.2 0.3 6.1 0.3 3.2 0 5.2-0.4 5.9-1.2 2.7-2.7 2.8-8.8 2.9-14.6 0.1-6.7 0.2-14.5 4.6-18.7 -0.5 0-1 0-1.6 0 -14.2 0-37.5 0-41.1 0C15.6 6.2 14.9 23.6 18.9 32.6z"/>
<path d="M90.1 29.7c1-3.3 1.5-7.3 1.5-11.2 0-9-2.7-18.8-8.6-18.8 -0.1 0-0.2 0-0.3 0L77.8-0.1c-0.3 0.2-0.8 0.3-1.1 0.4 0 0 0 0 0 0 -0.2 0-0.3 0-0.4 0 -4.5 0.1-7 1.8-8.4 4.9l8.9-0.1c-1.6 3.6-2.4 8.7-2.4 13.5 0 4.9 0.8 9.9 2.5 13.6l-12.3 0c-0.2 0.4-0.4 0.8-0.6 1.2 -0.2 0.4-0.4 0.7-0.6 1.1 -0.1 0.1-0.1 0.2-0.2 0.3 -0.3 0.4-0.5 0.7-0.9 1.1 0 0 0 0 0 0 0 0-0.1 0.1-0.1 0.1 -0.1 0.1-0.2 0.2-0.4 0.3 -0.2 0.1-0.4 0.3-0.6 0.4 0 0 0 0 0 0 -0.4 0.2-0.9 0.4-1.4 0.6 -1.3 0.4-2.9 0.6-4.9 0.7 -0.5 1.5-1.1 4.1 0 5.5l3.1 3.9 0 0.8c0 2.8-2.3 4.8-2.8 5.2l-3-3.8c0.3-0.2 0.5-0.5 0.7-0.8l-1.8-2.3c-2.2-2.7-1.8-6.3-1.2-8.7 -0.7 0-1.4-0.1-2-0.1 -2.1-0.1-4.3-0.3-6.2-0.3 -4.1 0-7.7 0.5-10.8 1 -1 0.2-2 0.3-3 0.4 -0.5 1.5-1.2 4.4-0.1 5.9l3.1 4 0 0.8c0 2.8-2.3 4.8-2.8 5.2l-3.1-3.8c0.3-0.2 0.6-0.6 0.7-0.9l-1.8-2.4c-2.1-2.8-1.8-6.3-1.2-8.7 -1.6-0.2-2.9-0.8-4-1.7h0c-0.8-0.6-1.4-1.4-2-2.4 -0.1-0.1-0.2-0.3-0.2-0.5 -0.1-0.2-0.2-0.4-0.3-0.6 -0.3-0.6-0.5-1.2-0.7-1.8l-5.6 0c-1-0.3-3.5-4.8-3.5-13.2 0-8.1 3.7-13.1 4.9-13.2L16.4 5.6c0.9-1.9 2-3.7 3.4-5.2L11.2 0.5c-5.4 0-10.1 8.6-10.1 18.4 0 8.9 2.7 18.4 8.6 18.4h2.4c-1.8 10.7-6.6 43 0.4 56.5 0.7 1.4 4.3 3.4 12.2 4.6 20.2 3.1 49.8-0.5 54.6-5.3 0.7-0.7 1.3-1.7 1.8-2.9 2-0.3 8.2-1.7 12.4-8.4C100.1 71.5 98.9 53.9 90.1 29.7zM35.6 87.1c-2.6 2-10.5 2.1-12.1 2.1 0 0 0 0 0 0 -3.9 0-9-0.4-10.8-2.3 -2.6-2.7-1.5-13-0.6-19.1 -0.1-1.9 0-5.8 2.2-7.2 1.9-1.2 8.7-1.3 11.6-1.3 6.4 0 7.4 0.6 7.8 0.9 3 1.8 3.1 5.6 2.6 7.8C37.7 75.5 38.6 84.8 35.6 87.1zM70.1 87.5c-2.6 2-10.5 2.1-12.1 2.1 0 0 0 0 0 0 -3.9 0-9-0.4-10.8-2.3 -2.6-2.7-1.5-13-0.6-19.1 -0.1-1.9 0-5.8 2.2-7.2 1.9-1.2 8.7-1.3 11.6-1.3 6.4 0 7.4 0.6 7.8 0.9 3 1.8 3.1 5.6 2.6 7.8C72.2 76 73.1 85.3 70.1 87.5zM85.9 12.3c-0.6-1.3-1.3-2.2-1.9-2.5 -0.5-0.3-1-0.3-1.4 0 -1.7 1.1-3.2 12.2-0.6 17.9 0.4 0.9 0.9 1.1 1.3 1.1 0.1 0 0.2 0 0.3 0 1.8-0.5 2.1-6.2 1.7-8.6l-1.6 0.2c0.3 2.2 0 5.1-0.4 6.3 -2.1-5.3-0.8-14.1 0.1-15.5 0.8 0.6 2.2 3.5 3.1 8 -0.1 7.9-2.4 12.3-3.4 12.8 -1-0.5-3.4-5.2-3.4-13.5 0-8.3 2.4-13 3.4-13.5C83.7 5.4 85.1 7.9 85.9 12.3zM87.3 77.7c-1.4 2.3-3.1 3.6-4.6 4.3 1.2-12.2-1-31-3.5-44.7h3.3c0.1 0 0.3 0 0.4 0 0.6 0 1.2-0.1 1.8-0.4C92.8 60.7 90.7 72.2 87.3 77.7z"/>
<path d="M24.7 71v5h-5.2v-5.4c-1.4-0.3-2.7-0.6-3.7-0.9 -0.9 6.8-1.1 13.3-0.3 14.5 0.4 0.3 2.9 1.1 8 1.1h0c5 0 8.8-0.7 9.7-1.3 0.8-1.3 0.6-7.7-0.4-14.4C30.7 70.1 27.5 70.8 24.7 71z"/>
<path d="M58.8 68.9c2.9-0.1 6.4-0.9 8.3-1.4 0.1-0.8 0.3-2.8-0.7-3.5 -0.5-0.2-2.5-0.4-5.9-0.4 -4.9 0-8.6 0.4-9.5 0.7 -0.3 0.5-0.5 1.9-0.5 3.3C52.5 68.1 56 69 58.8 68.9z"/>
<path d="M24.3 68.4c2.9-0.1 6.4-0.9 8.3-1.4 0.1-0.8 0.3-2.8-0.7-3.5 -0.5-0.2-2.5-0.4-5.9-0.4 -4.9 0-8.6 0.4-9.5 0.7 -0.3 0.5-0.5 1.9-0.5 3.3C18 67.7 21.5 68.6 24.3 68.4z"/>
<path d="M60.1 71.4v3.3h-5.2v-3.4c-1.7-0.3-3.3-0.7-4.6-1 -0.9 6.8-1.1 13.3-0.3 14.5 0.4 0.3 2.9 1.1 8 1.1h0c5 0 8.8-0.7 9.7-1.3 0.8-1.3 0.6-7.7-0.4-14.4C65.5 70.5 62.7 71.1 60.1 71.4z"/>

<!-- <text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Natalia Woodroffe</text>
<text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text> -->
</svg>
</a>
</div>
<header class="divider" id="main-header">
</header>
<main class="divider">
<div class="hero">
Expand All @@ -37,7 +18,7 @@
alt="image of a high mountain lake"
/>
<div class="logo logo--square">
<img src="/images/noun_Tent_2517.svg" alt="tent image for logo">
<img src="/images/noun_Tent_2517.svg" alt="tent image for logo" />
<div>Sleep<span class="highlight">Outside</span></div>
</div>
</div>
Expand All @@ -53,54 +34,9 @@
<section class="products">
<h2>Top Products</h2>
<ul class="product-list">
<li class="product-card">
<a href="product_pages/marmot-ajax-3.html">
<img
src="images/tents/marmot-ajax-tent-3-person-3-season-in-pale-pumpkin-terracotta~p~880rr_01~320.jpg"
alt="Marmot Ajax tent"
/>
<h3 class="card__brand">Marmot</h3>
<h2 class="card__name">Ajax Tent - 3-Person, 3-Season</h2>
<p class="product-card__price">$199.99</p></a>
</li>
<li class="product-card">
<a href="product_pages/northface-talus-4.html">
<img
src="images/tents/the-north-face-talus-tent-4-person-3-season-in-golden-oak-saffron-yellow~p~985rf_01~320.jpg"
alt="Talus Tent - 4-Person, 3-Season"
/>
<h3 class="card__brand">The North Face</h3>
<h2 class="card__name">Talus Tent - 4-Person, 3-Season</h2>
<p class="product-card__price">$199.99</p></a>
</li>
</li>
<li class="product-card">
<a href="product_pages/northface-alpine-3.html">
<img
src="images/tents/the-north-face-alpine-guide-tent-3-person-4-season-in-canary-yellow-high-rise-grey~p~985pr_01~320.jpg"
alt="Alpine Guide Tent - 3-Person, 4-Season"
/>
<h3 class="card__brand">The North Face</h3>
<h2 class="card__name">
Alpine Guide Tent - 3-Person, 4-Season
</h2>
<p class="product-card__price">$349.99</p></a>
</li>
<li class="product-card">
<a href="product_pages/cedar-ridge-rimrock-2.html">
<img
src="images/tents/cedar-ridge-rimrock-tent-2-person-3-season-in-rust-clay~p~344yj_01~320.jpg"
alt="Rimrock Tent - 2-Person, 3-Season"
/>
<h3 class="card__brand">Cedar Ridge</h3>
<h2 class="card__name">
Rimrock Tent - 2-Person, 3-Season
</h2>
<p class="product-card__price">$69.99</p></a>
</li>
</ul>
</section>
</main>
<footer>&copy;NOT a real business</footer>
<footer id="main-footer"></footer>
</body>
</html>
62 changes: 62 additions & 0 deletions src/js/Alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
export default class Alert {
constructor(jsonPath) {
this.path = jsonPath;
}

async getData() {
try {
const response = await fetch(this.path);
if (!response.ok) throw new Error("Bad Response");

const result = await response.json();
return result;
} catch (error) {
console.error("Alert request error: ", error);
return false;
}
}

async show(elementOrSelector) {
const alerts = await this.getData();
if (!alerts || alerts.length === 0) return;

const section = document.createElement("section");
section.classList.add("alert-list");
section.innerHTML = alerts
.map(
(alert) => `
<p class="alert" style="background: ${alert.background}; color: ${alert.color}">${alert.message}</p>
`,
)
.join("");

let alertParent;
if (typeof elementOrSelector === "string") {
alertParent = document.querySelector(elementOrSelector);
} else if (elementOrSelector instanceof Element) {
alertParent = elementOrSelector;
}

const htmlEl = document.querySelector("html");
const modalActiveClass = "modal-active";
htmlEl.classList.add(modalActiveClass);
alertParent.appendChild(section);

const top = (
window.innerHeight / 2 -
section.offsetHeight / 2 +
window.scrollY
).toFixed();
const left = (window.innerWidth / 2 - section.offsetWidth / 2).toFixed();
section.style = `left: ${left}px; top: ${top}px; opacity: 1;`;

htmlEl.addEventListener("click", function listenerFunc(e) {
if (e.target === alertParent || alertParent.contains(e.target)) {
return;
}

htmlEl.classList.remove(modalActiveClass);
htmlEl.removeEventListener("click", listenerFunc);
});
}
}
23 changes: 18 additions & 5 deletions src/js/ProductData.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,31 @@ function convertToJson(res) {
}
}

export default class ProductData {
export async function findProductById(id) {
const products = await getData();
return products.find((item) => item.Id === id);
}

export function getData(category = "tents") {
return fetch(`../json/${category}.json`)
.then(convertToJson)
.then((data) => data);
}


export default class ProductData {
constructor(category) {
this.category = category;
this.path = `../json/${this.category}.json`;
}
getData() {
getData() {
return fetch(this.path)
//.then(convertToJson).then((data) => data);
.then(convertToJson)
.then((data) => data);
.then((data) => data.filter((item) => item.Id != '989CG' && item.Id != '880RT'));
}
async findProductById(id) {
const products = await this.getData();
const products = await this.getData()
return products.find((item) => item.Id === id);
}
}
}
51 changes: 51 additions & 0 deletions src/js/ProductDetails.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { setLocalStorage, getLocalStorage } from "./utils.mjs";
import { findProductById } from "./ProductData.mjs";
import { qs, setContent } from "./utils.mjs";

function productDetailsTemplate(product) {
return `<section class="product-detail"> <h3>${product.Brand.Name}</h3>
<h2 class="divider">${product.NameWithoutBrand}</h2>
<img
class="divider"
src="${product.Image}"
alt="${product.NameWithoutBrand}"
/>
<p class="product-card__price">$${product.FinalPrice}</p>
<p class="product__color">${product.Colors[0].ColorName}</p>
<p class="product__description">
${product.DescriptionHtmlSimple}
</p>
<div class="product-detail__add">
<button id="addToCart" data-id="${product.Id}">Add to Cart</button>
</div></section>`;
}

export default class ProductDetails {
constructor(productId, dataSource) {
this.productId = productId;
this.product = {};
this.dataSource = dataSource;
}
async init() {
// use our datasource to get the details for the current product. findProductById will return a promise! use await or .then() to process it
this.product = await this.dataSource.findProductById(this.productId);
// once we have the product details we can render out the HTML
this.renderProductDetails("main");
// once the HTML is rendered we can add a listener to Add to Cart button
// Notice the .bind(this). Our callback will not work if we don't include that line. Review the readings from this week on 'this' to understand why.
document
.getElementById("addToCart")
.addEventListener("click", this.addToCart.bind(this));
}
addToCart() {
setLocalStorage("so-cart", this.product);
}
renderProductDetails(selector) {
const element = document.querySelector(selector);
element.insertAdjacentHTML(
"afterBegin",
productDetailsTemplate(this.product)
);
}
}

102 changes: 102 additions & 0 deletions src/js/ProductList.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// ProductList.mjs

import { renderListWithTemplate } from "./utils.mjs";

function productCardTemplate(product) {
return `<li class="product-card">
<a href="product_pages/index.html?product=${product.Id}">
<img
src="${product.Image}"
alt="Image of ${product.Name}"
/>
<h3 class="card__brand">${product.Brand.Name}</h3>
<h2 class="card__name">${product.Name}</h2>
<p class="product-card__price">$${product.FinalPrice}</p></a>
</li>`;
}

export default class ProductListing {
constructor(category, dataSource, listElement) {
// We passed in this information to make our class as reusable as possible.
// Being able to define these things when we use the class will make it very flexible
this.category = category;
this.dataSource = dataSource;
this.listElement = listElement;
}


async init() {
// our dataSource will return a Promise...so we can use await to resolve it.
const list = await this.dataSource.getData();

// render the list
this.renderList(list);
}
// render after doing the first stretch
renderList(list) {
renderListWithTemplate(productCardTemplate, this.listElement, list);
}
}

/*
async init() {
try {
this.products = await this.getData();
this.render();
} catch (error) {
console.error('Error fetching product data:', error);
}
}
*/

/*

async getData() {
const response = await fetch(this.dataSource);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const data = await response.json();
return data.filter(product => product.category === this.category);
}

render() {
this.listElement.innerHTML = ""; // Clear previous content
this.products.forEach(product => {
const card = this.createProductCard(product);
this.listElement.appendChild(card);
});
}

createProductCard(product) {
const card = document.createElement("div");
card.className = "product-card";
card.innerHTML = `
<h2>${product.name}</h2>
<p>${product.description}</p>
<span>$${product.price}</span>
`;
return card;
}
}
*/



/*
export default class ProductListing {
constructor(category, dataSource, listElement) {
// We passed in this information to make our class as reusable as possible.
// Being able to define these things when we use the class will make it very flexible
this.category = category;
this.dataSource = dataSource;
this.listElement = listElement;
}

async init() {
// our dataSource will return a Promise...so we can use await to resolve it.
const list = await this.dataSource.getData();
// render the list - to be completed
}
}
*/
2 changes: 1 addition & 1 deletion src/js/cart.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getLocalStorage } from "./utils.mjs";

function renderCartContents() {
const cartItems = getLocalStorage("so-cart");
const cartItems = getLocalStorage("so-cart") || [];
const htmlItems = cartItems.map((item) => cartItemTemplate(item));
document.querySelector(".product-list").innerHTML = htmlItems.join("");
}
Expand Down
Loading