Skip to content
Merged
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
8 changes: 4 additions & 4 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
<h2>Top Products</h2>
<ul class="product-list">
<li class="product-card">
<a href="product_pages/marmot-ajax-3.html">
<a href="product_pages/index.html?product=880RR">
<img
src="images/tents/marmot-ajax-tent-3-person-3-season-in-pale-pumpkin-terracotta~p~880rr_01~320.jpg"
alt="Marmot Ajax tent"
Expand All @@ -75,7 +75,7 @@ <h2 class="card__name">Ajax Tent - 3-Person, 3-Season</h2>
</a>
</li>
<li class="product-card">
<a href="product_pages/northface-talus-4.html">
<a href="product_pages/index.html?product=985RF">
<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"
Expand All @@ -86,7 +86,7 @@ <h2 class="card__name">Talus Tent - 4-Person, 3-Season</h2>
</a>
</li>
<li class="product-card">
<a href="product_pages/northface-alpine-3.html">
<a href="product_pages/index.html?product=985PR">
<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"
Expand All @@ -97,7 +97,7 @@ <h2 class="card__name">Alpine Guide Tent - 3-Person, 4-Season</h2>
</a>
</li>
<li class="product-card">
<a href="product_pages/cedar-ridge-rimrock-2.html">
<a href="product_pages/index.html?product=344YJ">
<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"
Expand Down
81 changes: 81 additions & 0 deletions src/js/ProductDetails.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { setLocalStorage, getLocalStorage } from './utils.mjs';

export default class ProductDetails {
constructor(productId, dataSource) {
this.productId = productId;
this.dataSource = dataSource;
this.product = {}; // Will hold the product object once fetched
}

/**
* Initializes the product details page by fetching data,
* rendering the HTML, and attaching the cart event listener.
*/
async init() {
// 1. Use the datasource to get the details for the current product.
this.product = await this.dataSource.findProductById(this.productId);

// 2. Render the HTML details onto the page
this.renderProductDetails('main.product-detail');

// 3. Add a listener to the Add to Cart button using .bind(this)
// so 'this' inside addToCart refers to this class instance.
document
.getElementById('addToCart')
.addEventListener('click', this.addToCart.bind(this));
}

/**
* Adds the currently viewed product to the localStorage cart.
*/
addToCart() {
Comment thread
itusebastian marked this conversation as resolved.
// 1. Retrieve existing cart or initialize an empty array if it doesn't exist
const cartItems = getLocalStorage('so-cart') || [];

// 2. Check if the product already exists in the cart by ID
const existingItem = cartItems.find(item => item.Id === this.product.Id);

if (existingItem) {
// If it exists, increment the quantity
existingItem.Quantity = (existingItem.Quantity || 1) + 1;
} else {
// If it's a new item, initialize quantity to 1 and push it
this.product.Quantity = 1;
cartItems.push(this.product);
}

// 3. Save the updated array back to localStorage
setLocalStorage('so-cart', cartItems);

// Visual feedback for the user
alert(`${this.product.NameWithoutBrand} added to cart!`);
}

/**
* Generates the HTML structure for the product details and injects it into the DOM.
* @param {string} selector - The CSS selector of the container element (e.g., 'main.product-detail')
*/
renderProductDetails(selector) {
const element = document.querySelector(selector);
if (!element) return;

// Generate the internal HTML based on the product data structures
element.innerHTML = `
<h3>${this.product.Brand.Name}</h3>
<h2 class="divider">${this.product.NameWithoutBrand}</h2>
<img
class="divider"
src="${this.product.Image}"
alt="${this.product.NameWithoutBrand}"
/>
<p class="product-card__price">$${this.product.FinalPrice}</p>
<p class="product__color">${this.product.Colors[0].ColorName}</p>
<p class="product__description">
${this.product.DescriptionHtmlSimple}
</p>
<div class="product-detail__add">
<button id="addToCart" data-id="${this.product.Id}">Add to Cart</button>
</div>
`;
}
}
31 changes: 5 additions & 26 deletions src/js/product.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
import { setLocalStorage, getLocalStorage } from './utils.mjs';
import { getParam } from './utils.mjs';
import ProductData from './ProductData.mjs';
import ProductDetails from './ProductDetails.mjs';

const productId = getParam('product');
const dataSource = new ProductData('tents');

// Retrieves the existing cart array, checks for duplicates, and updates local storage
function addProductToCart(product) {
const currentCart = getLocalStorage('so-cart') || [];
const existingItem = currentCart.find(item => item.Id === product.Id);

if (existingItem) {
existingItem.Quantity = (existingItem.Quantity || 1) + 1;
} else {
product.Quantity = 1;
currentCart.push(product);
}

setLocalStorage('so-cart', currentCart);
}

// 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 product = new ProductDetails(productId, dataSource);
product.init();
11 changes: 11 additions & 0 deletions src/js/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,14 @@ export function setClick(selector, callback) {
});
qs(selector).addEventListener('click', callback);
}

/**
* Retrieves the value of a specific parameter from the current URL's query string.
* @param {string} param - The name of the URL parameter to retrieve.
* @returns {string|null} The value of the parameter, or null if it doesn't exist.
*/
export function getParam(param) {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
return urlParams.get(param);
}
62 changes: 62 additions & 0 deletions src/product_pages/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!doctype html>

<html lang="en">
<head>
<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Sleep Outside | Cedar Ridge Rimrock 2-person tent</title>

<link rel="stylesheet" href="../css/style.css" />

<script src="../js/product.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>

<main class="product-detail divider">
</main>

<footer>&copy;NOT a real business</footer>
</body>
</html>