Feat: Dynamic gallery page with Fetch API and improved product card layout
Refactor gallery page to dynamically load product data using the Fetch API. This addresses a SyntaxError from incorrect script loading and updates the page to handle a new flattened data structure. - Replaced hardcoded product data with the Fetch API in `gallery-carousel.js`. - Updated selectors and logic to handle the new data structure. - Removed conflicting CSS rules for product cards. - Shortened all `longDescription` fields to 175 characters or less. - Widened the focused product card for better visibility without vertical scrolling. - Reduced vertical spacing between elements in product cards (title, description, price, button).
This commit is contained in:
@@ -1,130 +1,132 @@
|
||||
// Gallery Carousel .js
|
||||
const products = {
|
||||
trees: [
|
||||
{ id: 1, name: "Crepe Mertyl Tree", description: "Beautiful Crepe Mertyl tree.", price: 55.00, image: "images/trees/crepe-mertyl.jpg" },
|
||||
{ id: 2, name: "Silver Birch Tree", description: "Sturdy Silver Birch tree.", price: 45.00, image: "images/trees/silver-birch.jpg" },
|
||||
{ id: 3, name: "Apple Tree", description: "Fruitful apple tree.", price: 35.00, image: "images/trees/apple-tree.jpg" },
|
||||
{ id: 4, name: "Potted Spruce Tree", description: "Miniature Spruce in a white planter.", price: 35.00, image: "images/trees/potted-spruce.jpg" }
|
||||
],
|
||||
indoor: [
|
||||
{ id: 5, name: "Aloe Plant", description: "Thrives with 6-8 hours of direct sunlight a day.", price: 15.00, image: "images/indoor/aloe-plant.jpg" },
|
||||
{ id: 6, name: "Peperonia Plant", description: "Low-maintenance and perfect for spaces with lots of natural light.", price: 12.50, image: "images/indoor/peperonia.jpg" },
|
||||
{ id: 7, name: "String-of-Pearls Plant", description: "Best placed in east-facing windows or on shaded patios and balconies.", price: 20.00, image: "images/indoor/string-of-pearls.jpg" },
|
||||
{ id: 12, name: "Venus Fly Trap", description: "Perfect for young botanists or your fly problem!", price: 12.50, image: "images/indoor/venus-fly-trap.jpg"}
|
||||
],
|
||||
tools: [
|
||||
{ id: 8, name: "Watering Cans", description: "Galvanized aluminum watering cans (x2).", price: 25.00, image: "images/tools/watering-cans.jpg" },
|
||||
{ id: 9, name: "Potting Soil", description: "Premium-blend of nutrient-rich potting soil - 10 lb bag.", price: 8.00, image: "images/tools/potting-soil.png" },
|
||||
{ id: 10, name: "Bird House", description: "Handmade wooden bird house for hanging or mounting.", price: 11.00, image: "images/tools/birdhouse.jpg" },
|
||||
{ id: 11, name: "Gardening Tools", description: "Wooden handled gardening tools.", price: 20.00, image: "images/tools/gardening-tools.jpg"}
|
||||
]
|
||||
// --- Carousel State ---
|
||||
let allProducts = [];
|
||||
let selectedProducts = [];
|
||||
let currentIndex = 0;
|
||||
|
||||
// --- Helpers ---
|
||||
const getProductId = (product) => {
|
||||
return `${product.skuID}-${product.catNumber}-${product.productNumber}`;
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log("DOM fully loaded and parsed"); // Check if the DOM is ready
|
||||
|
||||
const productCarousel = document.getElementById('product-carousel');
|
||||
const scrollLeftButton = document.getElementById('scroll-left');
|
||||
const scrollRightButton = document.getElementById('scroll-right');
|
||||
const categoryButtons = document.querySelectorAll('.cat-btn');
|
||||
|
||||
// Scroll button functionality
|
||||
scrollLeftButton.addEventListener('click', () => {
|
||||
productCarousel.scrollBy({ left: -300, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
scrollRightButton.addEventListener('click', () => {
|
||||
productCarousel.scrollBy({ left: 300, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
// Map buttons to categories
|
||||
categoryButtons.forEach(button => {
|
||||
button.addEventListener('click', (event) => {
|
||||
event.preventDefault(); // Prevent default navigation
|
||||
const url = new URL(button.href); // Parse the URL
|
||||
const category = url.searchParams.get('category'); // Get category from the query parameter
|
||||
updateProductList(category); // Update the product list
|
||||
});
|
||||
});
|
||||
|
||||
// Load default category or the one from the current URL
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const defaultCategory = currentUrl.searchParams.get('category') || 'trees';
|
||||
updateProductList(defaultCategory);
|
||||
});
|
||||
|
||||
function updateScrollButtons() {
|
||||
const scrollLeftButton = document.getElementById('scroll-left');
|
||||
const scrollRightButton = document.getElementById('scroll-right');
|
||||
const productCarousel = document.getElementById('product-carousel');
|
||||
|
||||
scrollLeftButton.style.display = productCarousel.scrollLeft === 0 ? 'none' : 'block';
|
||||
scrollRightButton.style.display =
|
||||
productCarousel.scrollWidth - productCarousel.clientWidth === productCarousel.scrollLeft
|
||||
? 'none'
|
||||
: 'block';
|
||||
}
|
||||
|
||||
function updateProductList(category) {
|
||||
// --- Render the three visible items ---
|
||||
const renderCarousel = () => {
|
||||
const productList = document.getElementById("product-list");
|
||||
if (!productList) {
|
||||
console.error("Element with ID 'product-list' not found in the DOM.");
|
||||
productList.innerHTML = "";
|
||||
|
||||
if (selectedProducts.length === 0) return;
|
||||
|
||||
// Calculate indexes for left, center, right
|
||||
const leftIndex = (currentIndex - 1 + selectedProducts.length) % selectedProducts.length;
|
||||
const centerIndex = currentIndex;
|
||||
const rightIndex = (currentIndex + 1) % selectedProducts.length;
|
||||
|
||||
const visibleIndexes = [leftIndex, centerIndex, rightIndex];
|
||||
|
||||
visibleIndexes.forEach((idx, position) => {
|
||||
const product = selectedProducts[idx];
|
||||
const productId = getProductId(product);
|
||||
|
||||
const card = document.createElement("div");
|
||||
card.className = "product-card";
|
||||
if (position === 1) card.classList.add("is-centered");
|
||||
|
||||
card.dataset.productId = productId;
|
||||
card.innerHTML = `
|
||||
<img src="../images/_s-thumbs/${product.category}/${product.imageFile}" alt="${product.altText}">
|
||||
<h3>${product.productName}</h3>
|
||||
<p class="description">${position === 1 ? product.longDescription : product.shortDescription}</p>
|
||||
<p>$${product.price}</p>
|
||||
<button class="add-to-cart-btn" data-product-id="${productId}">Add to Cart</button>
|
||||
`;
|
||||
|
||||
// Event listeners
|
||||
card.querySelector('.add-to-cart-btn')
|
||||
.addEventListener('click', () => addToCart(productId, allProducts));
|
||||
|
||||
card.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (position === 0) prevProduct(); // left → move backward
|
||||
if (position === 2) nextProduct(); // right → move forward
|
||||
});
|
||||
|
||||
productList.appendChild(card);
|
||||
});
|
||||
};
|
||||
|
||||
// --- Navigation ---
|
||||
const nextProduct = () => {
|
||||
currentIndex = (currentIndex + 1) % selectedProducts.length;
|
||||
renderCarousel();
|
||||
};
|
||||
|
||||
const prevProduct = () => {
|
||||
currentIndex = (currentIndex - 1 + selectedProducts.length) % selectedProducts.length;
|
||||
renderCarousel();
|
||||
};
|
||||
|
||||
// --- Category Update ---
|
||||
const updateProductList = (category) => {
|
||||
const productList = document.getElementById("product-list");
|
||||
if (!productList) return console.error("Element with ID 'product-list' not found.");
|
||||
|
||||
selectedProducts = allProducts.filter(product => product.category === category);
|
||||
currentIndex = 0;
|
||||
|
||||
if (selectedProducts.length === 0) {
|
||||
console.error(`No products found for category: ${category}`);
|
||||
productList.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
productList.innerHTML = ""; // Clear existing products
|
||||
console.log(`Updating product list for category: ${category}`); // Debugging line
|
||||
renderCarousel();
|
||||
};
|
||||
|
||||
const selectedProducts = products[category];
|
||||
// --- Cart ---
|
||||
const addToCart = (productId, products) => {
|
||||
const product = products.find(p => getProductId(p) === productId);
|
||||
if (!product) return;
|
||||
|
||||
if (selectedProducts) {
|
||||
selectedProducts.forEach(product => {
|
||||
const productCard = document.createElement("div");
|
||||
productCard.className = "product-card";
|
||||
let cart = JSON.parse(sessionStorage.getItem('cart')) || [];
|
||||
const existing = cart.find(item => getProductId(item) === productId);
|
||||
|
||||
console.log(`Creating product card for: ${product.name}`); // Debugging line
|
||||
|
||||
productCard.innerHTML = `
|
||||
<img src="${product.image}" alt="${product.name}">
|
||||
<h3>${product.name}</h3>
|
||||
<p>${product.description}</p>
|
||||
<p>$${product.price}</p>
|
||||
<button class="add-to-cart-btn" data-product-id="${product.id}">Add to Cart</button>
|
||||
`;
|
||||
productList.appendChild(productCard);
|
||||
|
||||
// Add event listener for "Add to Cart" button
|
||||
const addToCartButton = productCard.querySelector('.add-to-cart-btn');
|
||||
addToCartButton.addEventListener('click', () => {
|
||||
addToCart(product.id);
|
||||
});
|
||||
});
|
||||
if (existing) {
|
||||
existing.quantity++;
|
||||
} else {
|
||||
console.error(`No products found for category: ${category}`);
|
||||
cart.push({ ...product, quantity: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
function addToCart(productId) {
|
||||
const product = findProductById(productId);
|
||||
if (product) {
|
||||
let cart = JSON.parse(sessionStorage.getItem('cart')) || [];
|
||||
const existingProduct = cart.find(item => item.id === productId);
|
||||
sessionStorage.setItem('cart', JSON.stringify(cart));
|
||||
alert(`${product.productName} has been added to your cart!`);
|
||||
};
|
||||
|
||||
if (existingProduct) {
|
||||
existingProduct.quantity++;
|
||||
} else {
|
||||
cart.push({ ...product, quantity: 1 });
|
||||
}
|
||||
// --- Init ---
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetch("../data/inventory.json")
|
||||
.then(r => r.json())
|
||||
.then(products => {
|
||||
allProducts = products;
|
||||
|
||||
sessionStorage.setItem('cart', JSON.stringify(cart));
|
||||
alert(`${product.name} has been added to your cart!`);
|
||||
}
|
||||
}
|
||||
const scrollLeftButton = document.getElementById('scroll-left');
|
||||
const scrollRightButton = document.getElementById('scroll-right');
|
||||
const categoryButtons = document.querySelectorAll(".cat-btn");
|
||||
|
||||
function findProductById(productId) {
|
||||
for (let category in products) {
|
||||
const product = products[category].find(item => item.id === productId);
|
||||
if (product) return product;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
scrollLeftButton.addEventListener('click', prevProduct);
|
||||
scrollRightButton.addEventListener('click', nextProduct);
|
||||
|
||||
categoryButtons.forEach(button => {
|
||||
button.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const url = new URL(button.href);
|
||||
const category = url.searchParams.get('category');
|
||||
updateProductList(category);
|
||||
});
|
||||
});
|
||||
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const defaultCategory = currentUrl.searchParams.get('category') || 'trees';
|
||||
updateProductList(defaultCategory);
|
||||
})
|
||||
.catch(err => console.error('Problem fetching products:', err));
|
||||
});
|
||||
|
Reference in New Issue
Block a user