419 lines
13 KiB
JavaScript
419 lines
13 KiB
JavaScript
// Product Management System
|
||
class ProductManager {
|
||
constructor() {
|
||
this.products = [];
|
||
this.categories = [];
|
||
this.pagination = {};
|
||
this.currentPage = 1;
|
||
this.itemsPerPage = 16;
|
||
this.filteredProducts = [];
|
||
this.selectedCategories = new Set();
|
||
}
|
||
|
||
// Load products from JSON file
|
||
async loadProducts() {
|
||
try {
|
||
const response = await fetch("/data/products.json");
|
||
const data = await response.json();
|
||
|
||
this.products = data.products;
|
||
this.categories = data.categories;
|
||
this.pagination = data.pagination;
|
||
this.filteredProducts = [...this.products];
|
||
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
this.updateResultsCount();
|
||
this.setupEventListeners();
|
||
this.renderCategoryFilters();
|
||
|
||
// Check for URL parameters and pre-select category
|
||
this.handleUrlParameters();
|
||
} catch (error) {
|
||
console.error("Error loading products:", error);
|
||
}
|
||
}
|
||
|
||
// Handle URL parameters for pre-selecting category filters
|
||
handleUrlParameters() {
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const category = urlParams.get("category");
|
||
|
||
if (category) {
|
||
// Pre-select the category in the filter
|
||
this.selectedCategories = new Set([category]);
|
||
this.applyFilters();
|
||
this.currentPage = 1;
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
this.updateResultsCount();
|
||
|
||
// Update the UI to show the filter is active
|
||
this.updateFilterUI(category);
|
||
}
|
||
}
|
||
|
||
// Update filter UI to show selected category
|
||
updateFilterUI(category) {
|
||
// Update filter button text to show active filter
|
||
const filterToggle = document.getElementById("filter-toggle");
|
||
if (filterToggle) {
|
||
const filterText = filterToggle.querySelector("span:last-child");
|
||
if (filterText) {
|
||
filterText.textContent = `Filter: ${category}`;
|
||
}
|
||
}
|
||
|
||
// Check the corresponding checkbox in the dropdown
|
||
setTimeout(() => {
|
||
const checkboxes = document.querySelectorAll(".category-checkbox");
|
||
checkboxes.forEach((checkbox) => {
|
||
if (checkbox.value === category) {
|
||
checkbox.checked = true;
|
||
}
|
||
});
|
||
}, 100);
|
||
}
|
||
|
||
// Render products in the grid
|
||
renderProducts() {
|
||
const productGrid = document.getElementById("product-grid");
|
||
if (!productGrid) return;
|
||
|
||
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
|
||
const endIndex = startIndex + this.itemsPerPage;
|
||
const productsToShow = this.filteredProducts.slice(startIndex, endIndex);
|
||
|
||
productGrid.innerHTML = productsToShow
|
||
.map((product) => this.createProductCard(product))
|
||
.join("");
|
||
this.updateResultsCount();
|
||
this.updatePagination();
|
||
}
|
||
|
||
// Create individual product card HTML
|
||
createProductCard(product) {
|
||
// Check if we're in comparison mode
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const returnTo = urlParams.get("returnTo");
|
||
const isComparisonMode = returnTo === "comparison";
|
||
|
||
return `
|
||
<div class="group relative bg-light-bg rounded-lg overflow-hidden hover:shadow-lg transition-shadow product-card" data-product-id="${
|
||
product.id
|
||
}">
|
||
<div class="relative h-80 overflow-hidden">
|
||
<img
|
||
src="${product.image}"
|
||
alt="${product.alt}"
|
||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||
/>
|
||
<!-- Hover Overlay -->
|
||
<div class="absolute inset-0 bg-dark-charcoal bg-opacity-70 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
|
||
<div class="text-center">
|
||
<button
|
||
class="bg-white text-uc-gold font-poppins font-semibold px-8 py-3 rounded-md hover:bg-uc-gold hover:text-white transition-colors ${
|
||
isComparisonMode ? "cursor-pointer" : ""
|
||
}"
|
||
onclick="productManager.viewProduct(${product.id})"
|
||
>
|
||
${isComparisonMode ? "Add to Comparison" : "View"}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="p-6">
|
||
<h3 class="font-poppins font-semibold text-2xl text-dark-charcoal mb-2">
|
||
${product.name}
|
||
</h3>
|
||
<p class="font-poppins font-medium text-base text-quick-silver">
|
||
${product.description}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Filter products by category
|
||
filterByCategory(category) {
|
||
// Single category helper (not used directly by UI)
|
||
this.selectedCategories = new Set([category]);
|
||
this.applyFilters();
|
||
this.currentPage = 1;
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
}
|
||
|
||
// Search products
|
||
searchProducts(query) {
|
||
if (!query.trim()) {
|
||
this.filteredProducts = [...this.products];
|
||
} else {
|
||
this.filteredProducts = this.products.filter(
|
||
(product) =>
|
||
product.name.toLowerCase().includes(query.toLowerCase()) ||
|
||
product.description.toLowerCase().includes(query.toLowerCase())
|
||
);
|
||
}
|
||
this.currentPage = 1;
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
}
|
||
|
||
// Apply selected category filters
|
||
applyFilters() {
|
||
if (
|
||
this.selectedCategories.size === 0 ||
|
||
this.selectedCategories.has("all")
|
||
) {
|
||
this.filteredProducts = [...this.products];
|
||
return;
|
||
}
|
||
this.filteredProducts = this.products.filter((product) =>
|
||
this.selectedCategories.has(product.category)
|
||
);
|
||
}
|
||
|
||
// Sort products
|
||
sortProducts(sortBy) {
|
||
switch (sortBy) {
|
||
case "name-asc":
|
||
this.filteredProducts.sort((a, b) => a.name.localeCompare(b.name));
|
||
break;
|
||
case "name-desc":
|
||
this.filteredProducts.sort((a, b) => b.name.localeCompare(a.name));
|
||
break;
|
||
default:
|
||
// Default sorting by ID
|
||
this.filteredProducts.sort((a, b) => a.id - b.id);
|
||
}
|
||
this.renderProducts();
|
||
}
|
||
|
||
// Change page
|
||
changePage(page) {
|
||
this.currentPage = page;
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
}
|
||
|
||
// Update pagination controls
|
||
updatePagination() {
|
||
const totalPages = Math.ceil(
|
||
this.filteredProducts.length / this.itemsPerPage
|
||
);
|
||
const paginationContainer = document.getElementById("pagination");
|
||
|
||
if (!paginationContainer) return;
|
||
|
||
let paginationHTML = "";
|
||
|
||
for (let i = 1; i <= totalPages; i++) {
|
||
const isActive = i === this.currentPage;
|
||
paginationHTML += `
|
||
<button
|
||
class="w-20 h-15 ${
|
||
isActive
|
||
? "bg-uc-gold text-white"
|
||
: "bg-floral-white text-black hover:bg-uc-gold hover:text-white"
|
||
} font-poppins font-normal text-xl rounded-lg flex items-center justify-center transition-colors"
|
||
onclick="productManager.changePage(${i})"
|
||
>
|
||
${i}
|
||
</button>
|
||
`;
|
||
}
|
||
|
||
if (totalPages > 1 && this.currentPage < totalPages) {
|
||
paginationHTML += `
|
||
<button
|
||
class="w-28 h-15 bg-floral-white text-black font-poppins font-light text-xl rounded-lg flex items-center justify-center hover:bg-uc-gold hover:text-white transition-colors"
|
||
onclick="productManager.changePage(${this.currentPage + 1})"
|
||
>
|
||
Next
|
||
</button>
|
||
`;
|
||
}
|
||
|
||
paginationContainer.innerHTML = paginationHTML;
|
||
}
|
||
|
||
// Build category multi-select dropdown
|
||
renderCategoryFilters() {
|
||
const container = document.getElementById("filter-categories");
|
||
if (!container) return;
|
||
|
||
const categoryOptions = this.categories
|
||
.map(
|
||
(c) => `
|
||
<label class="flex items-center gap-2 cursor-pointer">
|
||
<input type="checkbox" value="${c.id}" class="category-checkbox category-specific">
|
||
<span class="font-poppins text-sm text-black">${c.name}</span>
|
||
</label>
|
||
`
|
||
)
|
||
.join("");
|
||
|
||
const allOption = `
|
||
<label class="flex items-center gap-2 cursor-pointer">
|
||
<input type="checkbox" value="all" class="category-checkbox category-all">
|
||
<span class="font-poppins text-sm text-black">All</span>
|
||
</label>
|
||
`;
|
||
|
||
container.innerHTML = allOption + categoryOptions;
|
||
|
||
// Add event listeners for "All" checkbox behavior
|
||
const allCheckbox = container.querySelector(".category-all");
|
||
const specificCheckboxes = container.querySelectorAll(".category-specific");
|
||
|
||
if (allCheckbox) {
|
||
allCheckbox.addEventListener("change", (e) => {
|
||
const isChecked = e.target.checked;
|
||
specificCheckboxes.forEach((checkbox) => {
|
||
checkbox.checked = isChecked;
|
||
});
|
||
});
|
||
}
|
||
|
||
// Update "All" checkbox when specific categories change
|
||
specificCheckboxes.forEach((checkbox) => {
|
||
checkbox.addEventListener("change", () => {
|
||
const allChecked = Array.from(specificCheckboxes).every(
|
||
(c) => c.checked
|
||
);
|
||
const anyChecked = Array.from(specificCheckboxes).some(
|
||
(c) => c.checked
|
||
);
|
||
|
||
if (allChecked) {
|
||
allCheckbox.checked = true;
|
||
} else if (!anyChecked) {
|
||
allCheckbox.checked = false;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// View product details
|
||
viewProduct(productId) {
|
||
const product = this.products.find((p) => p.id === productId);
|
||
if (product) {
|
||
// Check if we're in comparison mode
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const returnTo = urlParams.get("returnTo");
|
||
const slot = urlParams.get("slot");
|
||
const product1Id = urlParams.get("product1");
|
||
const product2Id = urlParams.get("product2");
|
||
|
||
if (returnTo === "comparison" && slot) {
|
||
// Navigate back to comparison page with the selected product
|
||
let comparisonUrl = "product-comparison.html?";
|
||
|
||
if (slot === "1") {
|
||
// Replace product 1
|
||
comparisonUrl += `product1=${productId}`;
|
||
if (product2Id) {
|
||
comparisonUrl += `&product2=${product2Id}`;
|
||
}
|
||
} else {
|
||
// Replace product 2
|
||
if (product1Id) {
|
||
comparisonUrl += `product1=${product1Id}&`;
|
||
}
|
||
comparisonUrl += `product2=${productId}`;
|
||
}
|
||
|
||
console.log("Navigating to comparison page:", comparisonUrl);
|
||
window.location.href = comparisonUrl;
|
||
} else {
|
||
// Normal mode - navigate to product detail page
|
||
window.location.href = `product-detail.html?id=${productId}`;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Setup event listeners
|
||
setupEventListeners() {
|
||
// Filter dropdown toggle and outside click
|
||
const filterToggle = document.getElementById("filter-toggle");
|
||
const filterDropdown = document.getElementById("filter-dropdown");
|
||
if (filterToggle && filterDropdown) {
|
||
filterToggle.addEventListener("click", () => {
|
||
filterDropdown.classList.toggle("hidden");
|
||
});
|
||
document.addEventListener("click", (e) => {
|
||
if (
|
||
!filterDropdown.contains(e.target) &&
|
||
!filterToggle.contains(e.target)
|
||
) {
|
||
filterDropdown.classList.add("hidden");
|
||
}
|
||
});
|
||
}
|
||
|
||
// Sort dropdown
|
||
const sortSelect = document.querySelector("select");
|
||
if (sortSelect) {
|
||
sortSelect.addEventListener("change", (e) => {
|
||
this.sortProducts(e.target.value);
|
||
});
|
||
}
|
||
|
||
// Apply/clear category filters
|
||
const applyBtn = document.getElementById("filter-apply");
|
||
const clearBtn = document.getElementById("filter-clear");
|
||
if (applyBtn) {
|
||
applyBtn.addEventListener("click", () => {
|
||
const checks = Array.from(
|
||
document.querySelectorAll(".category-checkbox")
|
||
);
|
||
this.selectedCategories = new Set(
|
||
checks.filter((c) => c.checked).map((c) => c.value)
|
||
);
|
||
this.currentPage = 1;
|
||
this.applyFilters();
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
this.updateResultsCount();
|
||
const dropdown = document.getElementById("filter-dropdown");
|
||
if (dropdown) dropdown.classList.add("hidden");
|
||
});
|
||
}
|
||
if (clearBtn) {
|
||
clearBtn.addEventListener("click", () => {
|
||
const checks = Array.from(
|
||
document.querySelectorAll(".category-checkbox")
|
||
);
|
||
checks.forEach((c) => (c.checked = false));
|
||
this.selectedCategories.clear();
|
||
this.currentPage = 1;
|
||
this.applyFilters();
|
||
this.renderProducts();
|
||
this.updatePagination();
|
||
this.updateResultsCount();
|
||
});
|
||
}
|
||
}
|
||
|
||
// Update results count
|
||
updateResultsCount() {
|
||
const resultsElement = document.querySelector(".text-quick-silver");
|
||
if (resultsElement) {
|
||
const startIndex = (this.currentPage - 1) * this.itemsPerPage + 1;
|
||
const endIndex = Math.min(
|
||
startIndex + this.itemsPerPage - 1,
|
||
this.filteredProducts.length
|
||
);
|
||
resultsElement.textContent = `Showing ${startIndex}–${endIndex} of ${this.filteredProducts.length} results`;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Initialize product manager
|
||
const productManager = new ProductManager();
|
||
|
||
// Load products when DOM is ready
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
productManager.loadProducts();
|
||
});
|