2073 lines
68 KiB
JavaScript
2073 lines
68 KiB
JavaScript
// Global script initialization test
|
|
console.log("=== MAIN.JS LOADED ===");
|
|
console.log("Current pathname:", window.location.pathname);
|
|
console.log(
|
|
"Is comparison page:",
|
|
window.location.pathname.includes("product-comparison.html")
|
|
);
|
|
|
|
// Update year in footer and handle smooth scrolling
|
|
function initSite() {
|
|
// Update footer year
|
|
const footer = document.querySelector("footer p");
|
|
if (footer) {
|
|
const currentYear = new Date().getFullYear();
|
|
footer.innerHTML = footer.innerHTML.replace("2024", currentYear);
|
|
}
|
|
|
|
// Smooth scrolling for navigation links
|
|
const navLinks = document.querySelectorAll('.nav-link[href^="#"]');
|
|
navLinks.forEach((link) => {
|
|
link.addEventListener("click", function (e) {
|
|
e.preventDefault();
|
|
const targetId = this.getAttribute("href").substring(1);
|
|
const targetElement = document.getElementById(targetId);
|
|
if (targetElement) {
|
|
const headerHeight = 112;
|
|
const targetPosition = targetElement.offsetTop - headerHeight;
|
|
window.scrollTo({ top: targetPosition, behavior: "smooth" });
|
|
}
|
|
});
|
|
});
|
|
|
|
// Handle cross-page navigation links (links that point to other pages with anchors)
|
|
const crossPageLinks = document.querySelectorAll('.nav-link[href*=".html#"]');
|
|
crossPageLinks.forEach((link) => {
|
|
link.addEventListener("click", function () {
|
|
// allow default navigation
|
|
});
|
|
});
|
|
|
|
// Remove any lingering .active classes and avoid adding new ones
|
|
const allNavLinks = document.querySelectorAll(".nav-link");
|
|
allNavLinks.forEach((a) => a.classList.remove("active"));
|
|
|
|
// Do not add page-specific active state anymore
|
|
// const currentPage = window.location.pathname.split("/").pop() || "index.html";
|
|
// const currentPageLink = document.querySelector(`.nav-link[href="${currentPage}"]`);
|
|
// if (currentPageLink) currentPageLink.classList.add("active");
|
|
|
|
// Disable scroll-based active highlighting
|
|
// (Keeping function for potential future use, but it does nothing now.)
|
|
function updateActiveLink() {
|
|
// no-op: active highlighting disabled globally
|
|
}
|
|
window.addEventListener("scroll", updateActiveLink);
|
|
updateActiveLink();
|
|
|
|
// Quantity controls on product detail page
|
|
(function initQuantityControls() {
|
|
const decr = document.getElementById("qty-decr");
|
|
const incr = document.getElementById("qty-incr");
|
|
const valueEl = document.getElementById("qty-value");
|
|
if (!decr || !incr || !valueEl) return; // Not on product detail page
|
|
|
|
function parseQty() {
|
|
const n = parseInt(valueEl.textContent || "1", 10);
|
|
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
}
|
|
decr.addEventListener("click", () => {
|
|
const next = Math.max(1, parseQty() - 1);
|
|
valueEl.textContent = String(next);
|
|
});
|
|
incr.addEventListener("click", () => {
|
|
const next = parseQty() + 1;
|
|
valueEl.textContent = String(next);
|
|
});
|
|
})();
|
|
|
|
// Related products (dynamic)
|
|
(async function initRelated() {
|
|
const grid = document.getElementById("related-grid");
|
|
if (!grid) return;
|
|
|
|
// Don't run this function on product detail pages - let the product detail function handle it
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const productId = urlParams.get("id");
|
|
if (productId) return;
|
|
|
|
try {
|
|
const res = await fetch("data/products.json", { cache: "no-store" });
|
|
const data = await res.json();
|
|
const products = Array.isArray(data.products) ? data.products : [];
|
|
|
|
// Determine current product's category from the title text (fallback seating)
|
|
const title = document.querySelector("h1");
|
|
const currentName = (title?.textContent || "").trim();
|
|
const current = products.find(
|
|
(p) => (p.name || "").trim() === currentName
|
|
);
|
|
const category = current?.category || "seating";
|
|
|
|
// Filter related: same category but not the current product
|
|
const related = products.filter(
|
|
(p) => p.category === category && p.name !== currentName
|
|
);
|
|
|
|
let page = 1;
|
|
const pageSize = 4;
|
|
|
|
function render() {
|
|
grid.innerHTML = "";
|
|
const start = 0;
|
|
const end = page * pageSize; // cumulative show-more
|
|
related.slice(start, end).forEach((p) => {
|
|
const card = document.createElement("article");
|
|
card.className = "rounded-lg overflow-hidden bg-white shadow-sm";
|
|
|
|
// Distinct image background for Asgaard sofa to match others
|
|
const imageBgClass =
|
|
(p.name || "").toLowerCase() === "asgaard sofa"
|
|
? "bg-linen"
|
|
: "bg-white";
|
|
const panelBgClass = "bg-light-bg";
|
|
|
|
card.innerHTML = `
|
|
<div class=\"h-72 ${imageBgClass} flex items-center justify-center\">
|
|
<img src=\"${p.image}\" alt=\"${
|
|
p.alt || p.name
|
|
}\" class=\"w-full h-full object-cover\" />
|
|
</div>
|
|
<div class=\"${panelBgClass} p-6\">
|
|
<h3 class=\"font-poppins font-semibold text-2xl text-[#3A3A3A] mb-0\">${
|
|
p.name
|
|
}</h3>
|
|
</div>`;
|
|
grid.appendChild(card);
|
|
});
|
|
|
|
// Toggle show-more visibility
|
|
const btn = document.getElementById("related-show-more");
|
|
if (btn) {
|
|
const hasMore = related.length > page * pageSize;
|
|
btn.style.display = hasMore ? "inline-flex" : "none";
|
|
// Ensure centered label
|
|
btn.classList.add("inline-flex", "items-center", "justify-center");
|
|
}
|
|
}
|
|
|
|
const btn = document.getElementById("related-show-more");
|
|
if (btn) {
|
|
btn.addEventListener("click", () => {
|
|
page += 1;
|
|
render();
|
|
});
|
|
}
|
|
|
|
render();
|
|
} catch (e) {
|
|
console.error("Failed to load related products:", e);
|
|
}
|
|
})();
|
|
|
|
// Blog functionality (only runs on blog page)
|
|
(async function initBlog() {
|
|
const blogSearchInput = document.getElementById("blog-search-input");
|
|
const mainBlogContent = document.getElementById("main-blog-content");
|
|
const blogCategories = document.getElementById("blog-categories");
|
|
const recentPosts = document.getElementById("recent-posts");
|
|
const pagination = document.getElementById("blog-pagination");
|
|
const pageButtons = document.querySelectorAll(
|
|
"#blog-pagination .blog-page-btn"
|
|
);
|
|
const nextButton = document.getElementById("blog-next-btn");
|
|
|
|
if (!blogSearchInput || !mainBlogContent) return;
|
|
|
|
let allPosts = [];
|
|
let filteredPosts = [];
|
|
let activeTag = "";
|
|
let currentPage = 1;
|
|
const pageSize = 4;
|
|
|
|
// Load blog data from JSON
|
|
try {
|
|
const response = await fetch("data/blog.json", { cache: "no-store" });
|
|
const data = await response.json();
|
|
allPosts = Array.isArray(data.posts) ? data.posts : [];
|
|
|
|
// Sort posts by date (newest first)
|
|
allPosts.sort((a, b) => new Date(b.date) - new Date(a.date));
|
|
|
|
console.log("Blog posts loaded:", allPosts.length);
|
|
} catch (error) {
|
|
console.error("Failed to load blog posts:", error);
|
|
return;
|
|
}
|
|
|
|
function normalize(text) {
|
|
return (text || "")
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, " ")
|
|
.trim();
|
|
}
|
|
|
|
function formatDate(dateString) {
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString("en-US", {
|
|
day: "numeric",
|
|
month: "short",
|
|
year: "numeric",
|
|
});
|
|
}
|
|
|
|
function getFilteredPosts() {
|
|
const query = normalize(blogSearchInput.value.trim());
|
|
return allPosts.filter((post) => {
|
|
const title = post.title || "";
|
|
const excerpt = post.excerpt || "";
|
|
const haystack = normalize(`${title} ${excerpt}`);
|
|
const matchesQuery = query === "" || haystack.includes(query);
|
|
const matchesTag = activeTag === "" || post.tags.includes(activeTag);
|
|
return matchesQuery && matchesTag;
|
|
});
|
|
}
|
|
|
|
function renderBlogPost(post) {
|
|
const tagsHtml = post.tags
|
|
.map(
|
|
(tag) =>
|
|
`<span class="font-playfair font-normal text-base text-gray-500">${
|
|
tag.charAt(0).toUpperCase() + tag.slice(1)
|
|
}</span>`
|
|
)
|
|
.join(", ");
|
|
|
|
const contentHtml = post.content
|
|
.map((paragraph) => `<p class="mb-4">${paragraph}</p>`)
|
|
.join("");
|
|
|
|
return `
|
|
<article class="mb-16" data-tags="${post.tags.join(",")}" data-date="${
|
|
post.date
|
|
}">
|
|
<!-- Featured Image -->
|
|
<div class="mb-8">
|
|
<img
|
|
src="${post.image}"
|
|
alt="${post.alt || post.title}"
|
|
class="w-full h-96 object-cover rounded-lg"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Post Meta -->
|
|
<div class="flex items-center space-x-8 mb-6">
|
|
<!-- Admin -->
|
|
<div class="flex items-center space-x-2">
|
|
<img src="assets/icons/admin.png" alt="Admin" class="w-5 h-5" />
|
|
<span class="font-playfair font-normal text-base text-gray-500">
|
|
${post.author}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Date -->
|
|
<div class="flex items-center space-x-2">
|
|
<img src="assets/icons/calendar.png" alt="Calendar" class="w-5 h-5" />
|
|
<span class="font-playfair font-normal text-base text-gray-500">
|
|
${formatDate(post.date)}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Category -->
|
|
<div class="flex items-center space-x-2">
|
|
<img src="assets/icons/tag.png" alt="Tag" class="w-6 h-6" />
|
|
<span class="font-playfair font-normal text-base text-gray-500">
|
|
${
|
|
post.tags[0]
|
|
? post.tags[0].charAt(0).toUpperCase() +
|
|
post.tags[0].slice(1)
|
|
: "Uncategorized"
|
|
}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Post Title -->
|
|
<h2 class="font-playfair font-medium text-3xl md:text-4xl leading-tight text-black mb-6">
|
|
${post.title}
|
|
</h2>
|
|
|
|
<!-- Post Excerpt -->
|
|
<p class="font-playfair font-normal text-base leading-relaxed text-gray-500 mb-8 text-justify">
|
|
${post.excerpt}
|
|
</p>
|
|
|
|
<!-- Read More Link -->
|
|
<button
|
|
type="button"
|
|
class="read-more-toggle inline-block font-playfair font-normal text-base text-black border-b border-black hover:text-gray-600 hover:border-gray-600 transition-colors"
|
|
>
|
|
Read more
|
|
</button>
|
|
|
|
<!-- Full content (initially hidden) -->
|
|
<div class="full-content hidden font-playfair font-normal text-base leading-relaxed text-gray-500 mt-6 text-justify">
|
|
${contentHtml}
|
|
</div>
|
|
</article>
|
|
`;
|
|
}
|
|
|
|
function renderBlogPosts() {
|
|
const start = (currentPage - 1) * pageSize;
|
|
const end = start + pageSize;
|
|
const postsToShow = filteredPosts.slice(start, end);
|
|
|
|
mainBlogContent.innerHTML = postsToShow
|
|
.map((post) => renderBlogPost(post))
|
|
.join("");
|
|
|
|
// Re-initialize read more functionality
|
|
initReadMore();
|
|
}
|
|
|
|
function renderCategories() {
|
|
const tagCounts = {};
|
|
allPosts.forEach((post) => {
|
|
post.tags.forEach((tag) => {
|
|
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
|
});
|
|
});
|
|
|
|
const categoriesHtml = Object.entries(tagCounts)
|
|
.map(
|
|
([tag, count]) => `
|
|
<button
|
|
type="button"
|
|
class="flex justify-between items-center w-full text-left tag-filter px-4 py-3 rounded-lg border border-gray-300 hover:border-uc-gold hover:bg-gray-50 transition-colors"
|
|
data-tag="${tag}"
|
|
aria-pressed="false"
|
|
>
|
|
<span class="font-playfair font-normal text-base">
|
|
${tag.charAt(0).toUpperCase() + tag.slice(1)}
|
|
</span>
|
|
<span class="font-playfair font-normal text-base">
|
|
(${count})
|
|
</span>
|
|
</button>
|
|
`
|
|
)
|
|
.join("");
|
|
|
|
blogCategories.innerHTML = categoriesHtml;
|
|
|
|
// Add event listeners to category buttons
|
|
const categoryButtons = blogCategories.querySelectorAll(".tag-filter");
|
|
categoryButtons.forEach((btn) => {
|
|
btn.addEventListener("click", () => {
|
|
const clickedTag = btn.getAttribute("data-tag");
|
|
activeTag = activeTag === clickedTag ? "" : clickedTag;
|
|
|
|
categoryButtons.forEach((b) => {
|
|
b.classList.remove("border-uc-gold", "bg-gray-50", "text-uc-gold");
|
|
b.setAttribute("aria-pressed", "false");
|
|
});
|
|
|
|
if (activeTag) {
|
|
btn.classList.add("border-uc-gold", "bg-gray-50", "text-uc-gold");
|
|
btn.setAttribute("aria-pressed", "true");
|
|
}
|
|
|
|
currentPage = 1;
|
|
filteredPosts = getFilteredPosts();
|
|
renderBlogPosts();
|
|
renderPagination();
|
|
});
|
|
});
|
|
}
|
|
|
|
function renderRecentPosts() {
|
|
const recentPostsHtml = allPosts
|
|
.slice(0, 3)
|
|
.map(
|
|
(post) => `
|
|
<div class="flex space-x-4">
|
|
<img
|
|
src="${post.thumbnail}"
|
|
alt="${post.alt || post.title}"
|
|
class="w-20 h-20 object-cover rounded-lg"
|
|
/>
|
|
<div>
|
|
<h4 class="font-playfair font-normal text-sm text-black mb-2 leading-tight">
|
|
${post.title}
|
|
</h4>
|
|
<p class="font-playfair font-normal text-xs text-gray-500">
|
|
${formatDate(post.date)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
|
|
recentPosts.innerHTML = recentPostsHtml;
|
|
}
|
|
|
|
function renderPagination() {
|
|
const total = filteredPosts.length;
|
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
currentPage = Math.min(Math.max(1, currentPage), totalPages);
|
|
|
|
if (pagination) {
|
|
const hasQuery = blogSearchInput.value.trim().length > 0;
|
|
pagination.style.display = hasQuery ? "none" : "flex";
|
|
|
|
pageButtons.forEach((btn) => {
|
|
const p = Number(btn.getAttribute("data-page"));
|
|
btn.classList.toggle("bg-uc-gold", p === currentPage);
|
|
btn.classList.toggle("text-white", p === currentPage);
|
|
btn.classList.toggle("bg-linen", p !== currentPage);
|
|
btn.classList.toggle("text-black", p !== currentPage);
|
|
btn.style.display = p <= totalPages ? "inline-flex" : "none";
|
|
});
|
|
|
|
if (nextButton) {
|
|
nextButton.style.display = totalPages > 1 ? "inline-flex" : "none";
|
|
nextButton.disabled = currentPage >= totalPages;
|
|
}
|
|
}
|
|
}
|
|
|
|
function initReadMore() {
|
|
const articles = document.querySelectorAll("#main-blog-content article");
|
|
|
|
articles.forEach((article) => {
|
|
const excerptP = article.querySelector("p");
|
|
const readMoreBtn = article.querySelector(".read-more-toggle");
|
|
if (!excerptP || !readMoreBtn) return;
|
|
|
|
const fullExcerpt = excerptP.textContent || "";
|
|
const { text: truncated, truncated: isTruncated } = truncateToWords(
|
|
fullExcerpt,
|
|
80
|
|
);
|
|
|
|
if (isTruncated) {
|
|
excerptP.dataset.excerptFull = fullExcerpt;
|
|
excerptP.textContent = truncated;
|
|
readMoreBtn.style.display = "inline-block";
|
|
} else {
|
|
readMoreBtn.style.display = "none";
|
|
}
|
|
|
|
readMoreBtn.addEventListener("click", (ev) => {
|
|
ev.preventDefault();
|
|
if (excerptP.dataset.excerptFull) {
|
|
excerptP.textContent = excerptP.dataset.excerptFull;
|
|
delete excerptP.dataset.excerptFull;
|
|
}
|
|
const fullBlock = article.querySelector(".full-content");
|
|
if (fullBlock) fullBlock.classList.remove("hidden");
|
|
readMoreBtn.style.display = "none";
|
|
|
|
// Add "Show less" button at the end of the article
|
|
const showLessBtn = document.createElement("button");
|
|
showLessBtn.type = "button";
|
|
showLessBtn.className =
|
|
"show-less-toggle inline-block font-playfair font-normal text-base text-black border-b border-black hover:text-gray-600 hover:border-gray-600 transition-colors mt-4";
|
|
showLessBtn.textContent = "Show less";
|
|
showLessBtn.addEventListener("click", (ev) => {
|
|
ev.preventDefault();
|
|
// Restore truncated excerpt
|
|
excerptP.textContent = truncated;
|
|
excerptP.dataset.excerptFull = fullExcerpt;
|
|
// Hide full content
|
|
if (fullBlock) fullBlock.classList.add("hidden");
|
|
// Show "Read more" button again
|
|
readMoreBtn.style.display = "inline-block";
|
|
// Remove "Show less" button
|
|
showLessBtn.remove();
|
|
});
|
|
// Insert at the end of the article
|
|
article.appendChild(showLessBtn);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Event listeners
|
|
blogSearchInput.addEventListener("input", () => {
|
|
currentPage = 1;
|
|
filteredPosts = getFilteredPosts();
|
|
renderBlogPosts();
|
|
renderPagination();
|
|
});
|
|
|
|
pageButtons.forEach((btn) =>
|
|
btn.addEventListener("click", () => {
|
|
currentPage = Number(btn.getAttribute("data-page")) || 1;
|
|
renderBlogPosts();
|
|
renderPagination();
|
|
})
|
|
);
|
|
|
|
if (nextButton) {
|
|
nextButton.addEventListener("click", () => {
|
|
currentPage += 1;
|
|
renderBlogPosts();
|
|
renderPagination();
|
|
});
|
|
}
|
|
|
|
// Initialize everything
|
|
filteredPosts = getFilteredPosts();
|
|
renderBlogPosts();
|
|
renderCategories();
|
|
renderRecentPosts();
|
|
renderPagination();
|
|
})();
|
|
|
|
// Inline "Read more" expansion for blog posts
|
|
const articlesForExcerpt = document.querySelectorAll(
|
|
"#main-blog-content article"
|
|
);
|
|
|
|
function truncateToWords(text, maxWords) {
|
|
const words = (text || "").trim().split(/\s+/);
|
|
if (words.length <= maxWords) return { text, truncated: false };
|
|
return { text: words.slice(0, maxWords).join(" ") + "…", truncated: true };
|
|
}
|
|
|
|
articlesForExcerpt.forEach((article) => {
|
|
const excerptP = article.querySelector("p");
|
|
const readMoreBtn = article.querySelector(".read-more-toggle");
|
|
if (!excerptP || !readMoreBtn) return;
|
|
|
|
const fullExcerpt = excerptP.textContent || "";
|
|
const { text: truncated, truncated: isTruncated } = truncateToWords(
|
|
fullExcerpt,
|
|
80
|
|
);
|
|
|
|
if (isTruncated) {
|
|
// Store full text and show truncated preview
|
|
excerptP.dataset.excerptFull = fullExcerpt;
|
|
excerptP.textContent = truncated;
|
|
readMoreBtn.style.display = "inline-block";
|
|
} else {
|
|
// Nothing to expand; hide the control
|
|
readMoreBtn.style.display = "none";
|
|
}
|
|
|
|
// Click to expand (delegated fallback added below as well)
|
|
readMoreBtn.addEventListener("click", (ev) => {
|
|
ev.preventDefault();
|
|
if (excerptP.dataset.excerptFull) {
|
|
excerptP.textContent = excerptP.dataset.excerptFull;
|
|
delete excerptP.dataset.excerptFull;
|
|
}
|
|
const fullBlock = article.querySelector(".full-content");
|
|
if (fullBlock) fullBlock.classList.remove("hidden");
|
|
readMoreBtn.style.display = "none";
|
|
|
|
// Add "Show less" button at the end of the article
|
|
const showLessBtn = document.createElement("button");
|
|
showLessBtn.type = "button";
|
|
showLessBtn.className =
|
|
"show-less-toggle inline-block font-playfair font-normal text-base text-black border-b border-black hover:text-gray-600 hover:border-gray-600 transition-colors mt-4";
|
|
showLessBtn.textContent = "Show less";
|
|
showLessBtn.addEventListener("click", (ev) => {
|
|
ev.preventDefault();
|
|
// Restore truncated excerpt
|
|
excerptP.textContent = truncated;
|
|
excerptP.dataset.excerptFull = fullExcerpt;
|
|
// Hide full content
|
|
if (fullBlock) fullBlock.classList.add("hidden");
|
|
// Show "Read more" button again
|
|
readMoreBtn.style.display = "inline-block";
|
|
// Remove "Show less" button
|
|
showLessBtn.remove();
|
|
});
|
|
// Insert at the end of the article
|
|
article.appendChild(showLessBtn);
|
|
});
|
|
});
|
|
|
|
// Safety net: delegated handler in case markup changes
|
|
document.addEventListener("click", (e) => {
|
|
const trigger = e.target.closest(".read-more-toggle");
|
|
if (!trigger) return;
|
|
e.preventDefault();
|
|
const article = trigger.closest("article");
|
|
if (!article) return;
|
|
const excerptP = article.querySelector("p");
|
|
if (excerptP?.dataset?.excerptFull) {
|
|
excerptP.textContent = excerptP.dataset.excerptFull;
|
|
delete excerptP.dataset.excerptFull;
|
|
}
|
|
const fullBlock = article.querySelector(".full-content");
|
|
if (fullBlock) fullBlock.classList.remove("hidden");
|
|
trigger.style.display = "none";
|
|
});
|
|
|
|
// Footer newsletter subscribe validation and feedback (works on all pages)
|
|
const siteFooter = document.querySelector("footer");
|
|
if (siteFooter) {
|
|
const emailInputs = siteFooter.querySelectorAll('input[type="email"]');
|
|
emailInputs.forEach((emailInput) => {
|
|
const inputRow = emailInput.closest(".flex") || emailInput.parentElement;
|
|
let subscribeButton = inputRow ? inputRow.querySelector("button") : null;
|
|
if (!subscribeButton) {
|
|
subscribeButton = emailInput.parentElement?.querySelector("button");
|
|
}
|
|
if (!subscribeButton) return;
|
|
subscribeButton.type = "button";
|
|
let feedback = inputRow ? inputRow.nextElementSibling : null;
|
|
if (!(feedback instanceof HTMLElement) || !feedback.dataset.feedback) {
|
|
feedback = document.createElement("div");
|
|
feedback.dataset.feedback = "newsletter";
|
|
if (inputRow && inputRow.parentElement) {
|
|
inputRow.parentElement.insertBefore(feedback, inputRow.nextSibling);
|
|
}
|
|
}
|
|
feedback.className =
|
|
"hidden mt-3 rounded-lg px-4 py-3 font-playfair text-base";
|
|
function showMessage(text, type) {
|
|
feedback.textContent = text;
|
|
feedback.className =
|
|
"mt-3 rounded-lg px-4 py-3 font-playfair text-base";
|
|
if (type === "success") {
|
|
feedback.classList.add(
|
|
"bg-green-50",
|
|
"border",
|
|
"border-green-200",
|
|
"text-green-800"
|
|
);
|
|
} else {
|
|
feedback.classList.add(
|
|
"bg-red-50",
|
|
"border",
|
|
"border-red-200",
|
|
"text-red-800"
|
|
);
|
|
}
|
|
feedback.classList.remove("hidden");
|
|
setTimeout(() => feedback.classList.add("hidden"), 3000);
|
|
}
|
|
subscribeButton.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
const value = (emailInput.value || "").trim();
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
emailInput.classList.remove("border-red-500");
|
|
if (!emailRegex.test(value)) {
|
|
emailInput.classList.add("border-red-500");
|
|
showMessage("Please enter a valid email address.", "error");
|
|
emailInput.focus();
|
|
return;
|
|
}
|
|
showMessage("Thank you for subscribing!", "success");
|
|
emailInput.value = "";
|
|
subscribeButton.disabled = true;
|
|
setTimeout(() => (subscribeButton.disabled = false), 1200);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Product detail page functionality
|
|
(async function initProductDetail() {
|
|
console.log("Product detail script running...");
|
|
|
|
// Check if we're on the product detail page
|
|
const productTitle = document.querySelector("h1");
|
|
if (!productTitle) {
|
|
console.log("No h1 found, not on product detail page");
|
|
return;
|
|
}
|
|
|
|
// Get product ID from URL
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const productId = parseInt(urlParams.get("id"));
|
|
console.log("Product ID from URL:", productId);
|
|
|
|
if (!productId) {
|
|
console.log("No product ID found in URL");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Load product data
|
|
const response = await fetch("data/products.json");
|
|
const data = await response.json();
|
|
const product = data.products.find((p) => p.id === productId);
|
|
|
|
if (!product) {
|
|
console.error("Product not found:", productId);
|
|
return;
|
|
}
|
|
|
|
console.log("Loading product:", product);
|
|
|
|
// Update page title
|
|
document.title = `${product.name} - KHY`;
|
|
|
|
// Update the "Product Details" title with the actual product name
|
|
const productDetailsTitle = document.getElementById(
|
|
"product-details-title"
|
|
);
|
|
if (productDetailsTitle) {
|
|
productDetailsTitle.textContent = product.name;
|
|
}
|
|
|
|
// Update product title (the main product title, not the breadcrumb)
|
|
const allH1s = document.querySelectorAll("h1");
|
|
console.log("All H1 elements:", allH1s);
|
|
if (allH1s.length > 1) {
|
|
const mainProductTitle = allH1s[1]; // The second h1 should be the product title
|
|
console.log("Main product title element:", mainProductTitle);
|
|
if (mainProductTitle) {
|
|
mainProductTitle.textContent = product.name;
|
|
}
|
|
}
|
|
|
|
// Update product description - find by text content
|
|
const allParagraphs = document.querySelectorAll("p");
|
|
console.log("All paragraphs:", allParagraphs);
|
|
const descriptionEl = Array.from(allParagraphs).find(
|
|
(p) =>
|
|
p.textContent &&
|
|
p.textContent.includes(
|
|
"Setting the bar as one of the loudest speakers"
|
|
)
|
|
);
|
|
console.log("Description element:", descriptionEl);
|
|
if (descriptionEl) {
|
|
descriptionEl.textContent = product.description;
|
|
}
|
|
|
|
// Update main product image - find by alt text
|
|
const allImages = document.querySelectorAll("img");
|
|
console.log("All images:", allImages);
|
|
const mainImage = Array.from(allImages).find(
|
|
(img) => img.alt && img.alt.includes("Asgaard sofa")
|
|
);
|
|
console.log("Main image element:", mainImage);
|
|
if (mainImage) {
|
|
mainImage.src = product.image;
|
|
mainImage.alt = product.alt || product.name;
|
|
}
|
|
|
|
// Update thumbnail images - find by alt text
|
|
if (product.images && product.images.length > 0) {
|
|
const allImages = document.querySelectorAll("img");
|
|
const thumbnails = Array.from(allImages).filter(
|
|
(img) =>
|
|
img.alt &&
|
|
(img.alt.includes("Outdoor sofa set") ||
|
|
img.alt.includes("Stuart sofa") ||
|
|
img.alt.includes("Maya sofa"))
|
|
);
|
|
console.log("Thumbnail images found:", thumbnails);
|
|
product.images.forEach((imageSrc, index) => {
|
|
if (thumbnails[index]) {
|
|
thumbnails[index].src = imageSrc;
|
|
thumbnails[index].alt = product.alt || product.name;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update size options - use the specific size-button class
|
|
if (product.sizes) {
|
|
const sizeButtons = document.querySelectorAll(".size-button");
|
|
console.log("Size buttons found:", sizeButtons);
|
|
|
|
// Hide all size buttons first
|
|
sizeButtons.forEach((button) => {
|
|
button.style.display = "none";
|
|
});
|
|
|
|
// Show and update only the buttons we need
|
|
product.sizes.forEach((size, index) => {
|
|
if (sizeButtons[index]) {
|
|
sizeButtons[index].style.display = "flex";
|
|
sizeButtons[index].textContent = size;
|
|
sizeButtons[index].setAttribute("data-size", size);
|
|
// Set selected size
|
|
if (size === product.selectedSize) {
|
|
sizeButtons[index].classList.remove(
|
|
"bg-floral-white",
|
|
"text-black"
|
|
);
|
|
sizeButtons[index].classList.add("bg-uc-gold", "text-white");
|
|
sizeButtons[index].classList.add("selected");
|
|
} else {
|
|
sizeButtons[index].classList.remove(
|
|
"bg-uc-gold",
|
|
"text-white",
|
|
"selected"
|
|
);
|
|
sizeButtons[index].classList.add("bg-floral-white", "text-black");
|
|
}
|
|
}
|
|
});
|
|
|
|
// Add click event listeners for size buttons
|
|
sizeButtons.forEach((button) => {
|
|
button.addEventListener("click", function () {
|
|
// Remove selected state from all size buttons
|
|
sizeButtons.forEach((btn) => {
|
|
btn.classList.remove("bg-uc-gold", "text-white", "selected");
|
|
btn.classList.add("bg-floral-white", "text-black");
|
|
});
|
|
|
|
// Add selected state to clicked button
|
|
this.classList.remove("bg-floral-white", "text-black");
|
|
this.classList.add("bg-uc-gold", "text-white", "selected");
|
|
});
|
|
});
|
|
}
|
|
|
|
// Update color options - find only the rounded color buttons, not size buttons
|
|
if (product.colors) {
|
|
const allButtons = document.querySelectorAll("button");
|
|
const colorButtons = Array.from(allButtons).filter(
|
|
(button) =>
|
|
button.classList.contains("w-8") &&
|
|
button.classList.contains("h-8") &&
|
|
button.classList.contains("rounded-full") &&
|
|
!button.textContent // Color buttons should not have text content
|
|
);
|
|
console.log("Color buttons found:", colorButtons);
|
|
|
|
product.colors.forEach((color, index) => {
|
|
if (colorButtons[index]) {
|
|
colorButtons[index].style.backgroundColor = color.value;
|
|
colorButtons[index].setAttribute(
|
|
"data-color",
|
|
color.name || color.value
|
|
);
|
|
// Set selected color
|
|
if (color.selected) {
|
|
colorButtons[index].classList.add(
|
|
"border-2",
|
|
"border-black",
|
|
"selected"
|
|
);
|
|
} else {
|
|
colorButtons[index].classList.remove(
|
|
"border-2",
|
|
"border-black",
|
|
"selected"
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Add click event listeners for color buttons
|
|
colorButtons.forEach((button) => {
|
|
button.addEventListener("click", function () {
|
|
// Remove selected state from all color buttons
|
|
colorButtons.forEach((btn) => {
|
|
btn.classList.remove("border-2", "border-black", "selected");
|
|
});
|
|
|
|
// Add selected state to clicked button
|
|
this.classList.add("border-2", "border-black", "selected");
|
|
});
|
|
});
|
|
}
|
|
|
|
// Update product metadata - find by text content
|
|
const allSpans = document.querySelectorAll("span");
|
|
|
|
// Find and update Model No.
|
|
const modelNoLabel = Array.from(allSpans).find(
|
|
(span) => span.textContent === "Model No."
|
|
);
|
|
if (modelNoLabel && product.modelNo) {
|
|
const modelNoValue =
|
|
modelNoLabel.parentElement.querySelector("span:last-child");
|
|
if (modelNoValue) {
|
|
modelNoValue.textContent = product.modelNo;
|
|
}
|
|
}
|
|
|
|
// Find and update Category
|
|
const categoryLabel = Array.from(allSpans).find(
|
|
(span) => span.textContent === "Category"
|
|
);
|
|
if (categoryLabel && product.category) {
|
|
const categoryValue =
|
|
categoryLabel.parentElement.querySelector("span:last-child");
|
|
if (categoryValue) {
|
|
categoryValue.textContent =
|
|
product.category.charAt(0).toUpperCase() +
|
|
product.category.slice(1);
|
|
}
|
|
}
|
|
|
|
// Find and update Tags
|
|
const tagsLabel = Array.from(allSpans).find(
|
|
(span) => span.textContent === "Tags"
|
|
);
|
|
if (tagsLabel && product.tags) {
|
|
const tagsValue =
|
|
tagsLabel.parentElement.querySelector("span:last-child");
|
|
if (tagsValue) {
|
|
tagsValue.textContent = product.tags.join(", ");
|
|
}
|
|
}
|
|
|
|
// Find and update Dimensions
|
|
const dimensionsLabel = Array.from(allSpans).find(
|
|
(span) => span.textContent === "Dimension"
|
|
);
|
|
console.log("Dimensions label found:", dimensionsLabel);
|
|
console.log("Product dimensions:", product.dimensions);
|
|
|
|
if (dimensionsLabel && product.dimensions) {
|
|
const dimensionsValue =
|
|
dimensionsLabel.parentElement.querySelector("span:last-child");
|
|
console.log("Dimensions value element:", dimensionsValue);
|
|
if (dimensionsValue) {
|
|
dimensionsValue.textContent = product.dimensions;
|
|
console.log("Updated dimensions to:", product.dimensions);
|
|
}
|
|
}
|
|
|
|
// Update description content
|
|
if (product.descriptionLong) {
|
|
const descriptionContainer = document.querySelector(
|
|
".max-w-5xl.mx-auto.space-y-6"
|
|
);
|
|
if (descriptionContainer) {
|
|
const descriptionParagraphs =
|
|
descriptionContainer.querySelectorAll("p");
|
|
console.log("Description paragraphs found:", descriptionParagraphs);
|
|
|
|
// Update each paragraph with the product's description content
|
|
product.descriptionLong.forEach((paragraph, index) => {
|
|
if (descriptionParagraphs[index]) {
|
|
descriptionParagraphs[index].textContent = paragraph;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Update gallery images
|
|
if (product.galleryPairs) {
|
|
// Find gallery images by alt text
|
|
const allImages = document.querySelectorAll("img");
|
|
const galleryImages = Array.from(allImages).filter(
|
|
(img) =>
|
|
img.alt &&
|
|
(img.alt.includes("Sofa variant left") ||
|
|
img.alt.includes("Sofa variant right"))
|
|
);
|
|
console.log("Gallery images found:", galleryImages);
|
|
|
|
product.galleryPairs.forEach((pair, index) => {
|
|
if (galleryImages[index * 2]) {
|
|
galleryImages[index * 2].src = pair.left;
|
|
galleryImages[index * 2].alt = product.alt || product.name;
|
|
}
|
|
if (galleryImages[index * 2 + 1]) {
|
|
galleryImages[index * 2 + 1].src = pair.right;
|
|
galleryImages[index * 2 + 1].alt = product.alt || product.name;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update related products section
|
|
const relatedGrid = document.getElementById("related-grid");
|
|
if (relatedGrid && product.category) {
|
|
console.log("Current product category:", product.category);
|
|
console.log(
|
|
"All products:",
|
|
data.products.map((p) => ({
|
|
id: p.id,
|
|
name: p.name,
|
|
category: p.category,
|
|
}))
|
|
);
|
|
|
|
// Filter related products by category (excluding current product)
|
|
const allRelatedProducts = data.products.filter(
|
|
(p) => p.category === product.category && p.id !== product.id
|
|
);
|
|
|
|
console.log("All related products found:", allRelatedProducts);
|
|
|
|
let currentPage = 1;
|
|
const pageSize = 4;
|
|
|
|
function renderRelatedProducts() {
|
|
const start = 0;
|
|
const end = currentPage * pageSize;
|
|
const productsToShow = allRelatedProducts.slice(start, end);
|
|
|
|
relatedGrid.innerHTML = productsToShow
|
|
.map(
|
|
(p) => `
|
|
<a href="product-detail.html?id=${p.id}" class="block">
|
|
<div class="rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-lg transition-shadow">
|
|
<div class="h-72 ${
|
|
p.name.toLowerCase().includes("asgaard")
|
|
? "bg-linen"
|
|
: "bg-white"
|
|
} flex items-center justify-center">
|
|
<img src="${p.image}" alt="${
|
|
p.alt || p.name
|
|
}" class="w-full h-full object-cover" />
|
|
</div>
|
|
<div class="bg-light-bg p-6">
|
|
<h3 class="font-poppins font-semibold text-2xl text-[#3A3A3A] mb-0">${
|
|
p.name
|
|
}</h3>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
`
|
|
)
|
|
.join("");
|
|
|
|
// Handle "Show More" button visibility
|
|
const showMoreBtn = document.getElementById("related-show-more");
|
|
if (showMoreBtn) {
|
|
const hasMore = allRelatedProducts.length > end;
|
|
showMoreBtn.style.display = hasMore ? "inline-flex" : "none";
|
|
showMoreBtn.classList.add(
|
|
"inline-flex",
|
|
"items-center",
|
|
"justify-center"
|
|
);
|
|
}
|
|
}
|
|
|
|
// Add event listener for "Show More" button
|
|
const showMoreBtn = document.getElementById("related-show-more");
|
|
if (showMoreBtn) {
|
|
showMoreBtn.addEventListener("click", () => {
|
|
currentPage += 1;
|
|
renderRelatedProducts();
|
|
});
|
|
}
|
|
|
|
// Initial render
|
|
renderRelatedProducts();
|
|
}
|
|
} catch (error) {
|
|
console.error("Error loading product data:", error);
|
|
}
|
|
})();
|
|
}
|
|
|
|
// Product Comparison Page Functionality
|
|
function initProductComparison() {
|
|
console.log("=== INITIALIZING PRODUCT COMPARISON ===");
|
|
|
|
// Prevent running twice (it's invoked in multiple places)
|
|
if (window.__cmpInitialized) {
|
|
console.log("[Comparison] Already initialized, skipping");
|
|
return;
|
|
}
|
|
window.__cmpInitialized = true;
|
|
|
|
// Get product IDs from URL parameters
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const product1Id = urlParams.get("product1");
|
|
const product2Id = urlParams.get("product2");
|
|
|
|
console.log("URL Parameters:", { product1Id, product2Id });
|
|
|
|
// Keep references to the two products for event handlers
|
|
let cmpProduct1 = null;
|
|
let cmpProduct2 = null;
|
|
|
|
// Fetch product data
|
|
fetch("data/products.json")
|
|
.then((response) => {
|
|
console.log("Fetch response status:", response.status);
|
|
return response.json();
|
|
})
|
|
.then((data) => {
|
|
console.log("Products data loaded:", data);
|
|
console.log("Total products:", data.products.length);
|
|
|
|
// Find products by ID
|
|
const product1 = data.products.find((p) => p.id == product1Id);
|
|
const product2 = data.products.find((p) => p.id == product2Id);
|
|
|
|
console.log("Found products:", { product1, product2 });
|
|
|
|
// Update product cards
|
|
if (product1) {
|
|
console.log("Updating product card 1 with:", product1.name);
|
|
cmpProduct1 = product1;
|
|
updateProductCard(1, product1);
|
|
updateComparisonTable(product1, 1);
|
|
}
|
|
|
|
if (product2) {
|
|
console.log("Updating product card 2 with:", product2.name);
|
|
cmpProduct2 = product2;
|
|
updateProductCard(2, product2);
|
|
updateComparisonTable(product2, 2);
|
|
}
|
|
|
|
// Populate dropdown
|
|
populateProductDropdown(data.products);
|
|
|
|
// Update View More link with current comparison state
|
|
updateViewMoreLink(product1Id, product2Id);
|
|
|
|
// Wire Add To Quote buttons (first -> product1, second -> product2)
|
|
wireComparisonAddToQuote(cmpProduct1, cmpProduct2);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error loading products:", error);
|
|
});
|
|
}
|
|
|
|
// Bind the two Add To Quote buttons on the comparison page
|
|
function wireComparisonAddToQuote(prod1, prod2) {
|
|
try {
|
|
const addButtons = Array.from(document.querySelectorAll("button")).filter(
|
|
(b) => (b.textContent || "").trim() === "Add To Quote"
|
|
);
|
|
|
|
if (addButtons.length === 0) {
|
|
console.log("[Comparison] No Add To Quote buttons found");
|
|
return;
|
|
}
|
|
|
|
// Ensure deterministic order: the first encountered is for product 1
|
|
const btn1 = addButtons[0] || null;
|
|
const btn2 = addButtons[1] || null;
|
|
|
|
function toQuotePayload(p) {
|
|
if (!p) return null;
|
|
return {
|
|
id: Number(p.id),
|
|
name: p.name || "Product",
|
|
image: p.image || "",
|
|
color:
|
|
(p.colors &&
|
|
p.colors[0] &&
|
|
(p.colors[0].name || p.colors[0].value)) ||
|
|
"Default",
|
|
size: (p.sizes && p.sizes[0]) || "Standard",
|
|
quantity: 1,
|
|
};
|
|
}
|
|
|
|
if (btn1) {
|
|
btn1.type = btn1.getAttribute("type") || "button";
|
|
btn1.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const payload = toQuotePayload(prod1);
|
|
if (!payload) return;
|
|
payload.quantity = 1; // force 1 for comparison adds
|
|
if (typeof addToQuote === "function") {
|
|
addToQuote(payload);
|
|
} else {
|
|
addToQuoteFallback(payload);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (btn2) {
|
|
btn2.type = btn2.getAttribute("type") || "button";
|
|
btn2.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const payload = toQuotePayload(prod2);
|
|
if (!payload) return;
|
|
payload.quantity = 1; // force 1 for comparison adds
|
|
if (typeof addToQuote === "function") {
|
|
addToQuote(payload);
|
|
} else {
|
|
addToQuoteFallback(payload);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Delegated fallback (in case DOM changes after load)
|
|
document.addEventListener("click", (e) => {
|
|
const t = e.target.closest && e.target.closest("button");
|
|
if (!t) return;
|
|
const label = (t.textContent || "").trim();
|
|
if (label !== "Add To Quote") return;
|
|
// Determine which button index this is relative to current NodeList
|
|
const currentButtons = Array.from(
|
|
document.querySelectorAll("button")
|
|
).filter((b) => (b.textContent || "").trim() === "Add To Quote");
|
|
const idx = currentButtons.indexOf(t);
|
|
if (idx === 0) {
|
|
const payload = toQuotePayload(prod1);
|
|
if (payload) {
|
|
payload.quantity = 1;
|
|
(typeof addToQuote === "function" ? addToQuote : addToQuoteFallback)(
|
|
payload
|
|
);
|
|
}
|
|
} else if (idx === 1) {
|
|
const payload = toQuotePayload(prod2);
|
|
if (payload) {
|
|
payload.quantity = 1;
|
|
(typeof addToQuote === "function" ? addToQuote : addToQuoteFallback)(
|
|
payload
|
|
);
|
|
}
|
|
}
|
|
});
|
|
} catch (err) {
|
|
console.error("[Comparison] Failed to wire Add To Quote buttons:", err);
|
|
}
|
|
}
|
|
|
|
function updateProductCard(slotNumber, product) {
|
|
console.log(`=== UPDATING PRODUCT CARD ${slotNumber} ===`);
|
|
console.log("Product data:", product);
|
|
|
|
const cardContainer = document.querySelector(`.product-card-${slotNumber}`);
|
|
console.log("Card container found:", cardContainer);
|
|
|
|
if (cardContainer) {
|
|
const imageContainer = cardContainer.querySelector("div");
|
|
const title = cardContainer.querySelector("p");
|
|
|
|
console.log("Image container found:", imageContainer);
|
|
console.log("Title element found:", title);
|
|
|
|
if (imageContainer) {
|
|
console.log("Replacing image container with:", product.image);
|
|
// Replace the placeholder div with an image
|
|
imageContainer.innerHTML = `<img src="${product.image}" alt="${product.name}" class="w-full h-full object-cover rounded-lg">`;
|
|
}
|
|
if (title) {
|
|
console.log("Updating title to:", product.name);
|
|
title.textContent = product.name;
|
|
}
|
|
} else {
|
|
console.log(`Product card container .product-card-${slotNumber} not found`);
|
|
}
|
|
}
|
|
|
|
function updateComparisonTable(product, slotNumber) {
|
|
console.log(`=== UPDATING COMPARISON TABLE FOR SLOT ${slotNumber} ===`);
|
|
console.log("Product:", product);
|
|
|
|
// Update all sections
|
|
const sections = ["general", "product", "dimensions", "warranty"];
|
|
|
|
sections.forEach((section) => {
|
|
console.log(`Updating ${section} section...`);
|
|
updateTableSection(section, product, slotNumber);
|
|
});
|
|
}
|
|
|
|
function updateTableSection(sectionName, product, slotNumber) {
|
|
console.log(
|
|
`=== UPDATING TABLE SECTION: ${sectionName} (SLOT ${slotNumber}) ===`
|
|
);
|
|
const sectionData = getProductSectionData(product, sectionName);
|
|
|
|
// Find the section header
|
|
const section = document.querySelector(
|
|
`.comparison-table .${sectionName}-section`
|
|
);
|
|
if (!section) {
|
|
console.log(`Section ${sectionName}-section not found`);
|
|
return;
|
|
}
|
|
|
|
// Find the table that comes after this section header
|
|
const sectionTable = section.nextElementSibling;
|
|
if (!sectionTable) {
|
|
console.log(`Table after ${sectionName}-section not found`);
|
|
return;
|
|
}
|
|
|
|
// Find all flex rows and filter to only those with data columns
|
|
const allRows = sectionTable.querySelectorAll(".flex.items-center");
|
|
const rows = Array.from(allRows).filter((row) =>
|
|
row.querySelector(".column-1")
|
|
);
|
|
console.log(
|
|
`Found ${rows.length} data rows in ${sectionName} section (out of ${allRows.length} total rows)`
|
|
);
|
|
|
|
if (rows.length > 0 && sectionData) {
|
|
console.log("Updating rows with data:", sectionData);
|
|
|
|
// Update each row with the corresponding data
|
|
rows.forEach((row, index) => {
|
|
if (index < sectionData.length) {
|
|
const column = row.querySelector(`.column-${slotNumber}`);
|
|
if (column) {
|
|
console.log(
|
|
`Updating row ${index + 1} with data: ${sectionData[index]}`
|
|
);
|
|
console.log(`Row element:`, row);
|
|
console.log(`Column element:`, column);
|
|
console.log(`Column text before update: "${column.textContent}"`);
|
|
column.textContent = sectionData[index];
|
|
console.log(`Column text after update: "${column.textContent}"`);
|
|
} else {
|
|
console.log(`Column ${slotNumber} not found in row ${index + 1}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log("Table section updated successfully.");
|
|
} else {
|
|
console.log("No rows found or section data is empty, skipping update.");
|
|
console.log("Rows found:", rows.length);
|
|
console.log("Section data:", sectionData);
|
|
}
|
|
}
|
|
|
|
function getProductSectionData(product, sectionName) {
|
|
console.log(`=== GETTING PRODUCT SECTION DATA: ${sectionName} ===`);
|
|
console.log("Product:", product);
|
|
|
|
let sectionData;
|
|
switch (sectionName) {
|
|
case "general":
|
|
sectionData = [
|
|
product.salesPackage || "N/A", // Sales Package
|
|
product.modelNo || "N/A", // Model Number
|
|
product.additionalInformation?.Material || "N/A", // Secondary Material
|
|
product.configuration || "N/A", // Configuration
|
|
product.additionalInformation?.Upholstery || "N/A", // Upholstery Material
|
|
product.colors?.[0]?.name || "N/A", // Upholstery Color
|
|
];
|
|
break;
|
|
case "product":
|
|
sectionData = [
|
|
product.fillingMaterial || "N/A", // Filling Material
|
|
product.finishType || "N/A", // Finish Type
|
|
product.adjustableHeadrest || "N/A", // Adjustable Headrest
|
|
product.maxLoadCapacity || "N/A", // Maximum Load Capacity
|
|
product.originOfManufacture || "N/A", // Origin of Manufacture
|
|
];
|
|
break;
|
|
case "dimensions":
|
|
const dims = product.dimensions?.split(" x ") || [];
|
|
sectionData = [
|
|
dims[0] || "N/A", // Width
|
|
dims[1] || "N/A", // Height
|
|
dims[2] || "N/A", // Depth
|
|
product.weight || "N/A", // Weight
|
|
product.seatHeight || "N/A", // Seat Height
|
|
product.legHeight || "N/A", // Leg Height
|
|
];
|
|
break;
|
|
case "warranty":
|
|
sectionData = [
|
|
product.additionalInformation?.Warranty || "N/A", // Warranty Summary
|
|
product.warrantyServiceType || "N/A", // Warranty Service Type
|
|
product.coveredInWarranty || "N/A", // Covered in Warranty
|
|
product.notCoveredInWarranty || "N/A", // Not Covered in Warranty
|
|
product.additionalInformation?.Warranty || "N/A", // Domestic Warranty
|
|
];
|
|
break;
|
|
default:
|
|
sectionData = [];
|
|
}
|
|
|
|
console.log(`Section data for ${sectionName}:`, sectionData);
|
|
console.log(`Detailed breakdown for ${sectionName}:`);
|
|
sectionData.forEach((item, index) => {
|
|
console.log(` Item ${index + 1}: "${item}"`);
|
|
});
|
|
|
|
return sectionData;
|
|
}
|
|
|
|
function populateProductDropdown(products) {
|
|
const dropdown = document.getElementById("product-dropdown");
|
|
if (dropdown) {
|
|
// Clear existing options except the first one
|
|
while (dropdown.children.length > 1) {
|
|
dropdown.removeChild(dropdown.lastChild);
|
|
}
|
|
|
|
// Add all products
|
|
products.forEach((product) => {
|
|
const option = document.createElement("option");
|
|
option.value = product.id;
|
|
option.textContent = product.name;
|
|
dropdown.appendChild(option);
|
|
});
|
|
|
|
// Add event listener for product selection
|
|
dropdown.addEventListener("change", function () {
|
|
const selectedProductId = this.value;
|
|
console.log("Product selected from dropdown:", selectedProductId);
|
|
|
|
if (selectedProductId && selectedProductId !== "Choose a Product") {
|
|
// Find the selected product
|
|
const selectedProduct = products.find((p) => p.id == selectedProductId);
|
|
|
|
if (selectedProduct) {
|
|
console.log("Selected product found:", selectedProduct);
|
|
|
|
// Update the second product slot
|
|
updateProductCard(2, selectedProduct);
|
|
updateComparisonTable(selectedProduct, 2);
|
|
|
|
// Update URL to include the second product
|
|
updateURLParameter("product2", selectedProductId);
|
|
|
|
// Reset dropdown to default option
|
|
this.value = "Choose a Product";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateURLParameter(param, value) {
|
|
const url = new URL(window.location);
|
|
url.searchParams.set(param, value);
|
|
window.history.replaceState({}, "", url);
|
|
}
|
|
|
|
function updateViewMoreLink(product1Id, product2Id) {
|
|
const viewMoreLink = document.querySelector(
|
|
'a[href*="product-catalog.html"]'
|
|
);
|
|
if (viewMoreLink) {
|
|
// Determine which slot is available (1 or 2)
|
|
let availableSlot = 1;
|
|
if (!product1Id) {
|
|
availableSlot = 1;
|
|
} else if (!product2Id) {
|
|
availableSlot = 2;
|
|
} else {
|
|
// Both slots are filled, default to slot 2 for replacement
|
|
availableSlot = 2;
|
|
}
|
|
|
|
let newHref = `product-catalog.html?returnTo=comparison&slot=${availableSlot}`;
|
|
if (product1Id) newHref += `&product1=${product1Id}`;
|
|
if (product2Id) newHref += `&product2=${product2Id}`;
|
|
|
|
viewMoreLink.href = newHref;
|
|
console.log("Updated View More link:", newHref);
|
|
}
|
|
}
|
|
|
|
// Initialize product comparison if on comparison page
|
|
console.log("=== CHECKING IF COMPARISON PAGE INITIALIZATION SHOULD RUN ===");
|
|
console.log(
|
|
"Pathname includes product-comparison.html:",
|
|
window.location.pathname.includes("product-comparison.html")
|
|
);
|
|
|
|
if (window.location.pathname.includes("product-comparison.html")) {
|
|
console.log("Product comparison page detected, initializing immediately");
|
|
initProductComparison();
|
|
|
|
// Also try on DOMContentLoaded as backup
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
console.log("Product comparison page DOMContentLoaded backup");
|
|
initProductComparison();
|
|
});
|
|
}
|
|
|
|
// Product Detail Page - Compare Products functionality
|
|
function initProductDetailCompare() {
|
|
console.log("=== INITIALIZING PRODUCT DETAIL COMPARE ===");
|
|
console.log("Current URL:", window.location.href);
|
|
console.log("Pathname:", window.location.pathname);
|
|
|
|
// Try to find the button immediately
|
|
let compareButton = document.getElementById("compare-products-btn");
|
|
console.log("Button found by ID:", compareButton);
|
|
|
|
if (!compareButton) {
|
|
console.log("Button not found by ID, trying text search...");
|
|
const buttons = document.querySelectorAll("button");
|
|
console.log("Total buttons found:", buttons.length);
|
|
|
|
buttons.forEach((button, index) => {
|
|
console.log(`Button ${index}: "${button.textContent.trim()}"`);
|
|
});
|
|
|
|
compareButton = Array.from(buttons).find(
|
|
(button) => button.textContent.trim() === "Compare Products"
|
|
);
|
|
console.log("Button found by text search:", compareButton);
|
|
}
|
|
|
|
if (compareButton) {
|
|
console.log("=== ADDING CLICK LISTENER ===");
|
|
|
|
// Remove any existing listeners first
|
|
compareButton.removeEventListener("click", handleCompareClick);
|
|
compareButton.addEventListener("click", handleCompareClick);
|
|
|
|
// Also add a direct onclick handler as backup
|
|
compareButton.onclick = handleCompareClick;
|
|
|
|
console.log("Click listener added successfully");
|
|
console.log("Button element:", compareButton);
|
|
console.log("Button text content:", compareButton.textContent);
|
|
} else {
|
|
console.log("=== BUTTON NOT FOUND ===");
|
|
console.log("Compare Products button not found");
|
|
}
|
|
}
|
|
|
|
// Separate function for the click handler
|
|
function handleCompareClick(event) {
|
|
console.log("Compare Products button clicked");
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
// Get current product ID from URL
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const productId = urlParams.get("id");
|
|
|
|
console.log("Product ID from URL:", productId);
|
|
|
|
if (productId) {
|
|
// Navigate to comparison page with current product as product1
|
|
const comparisonUrl = `product-comparison.html?product1=${productId}`;
|
|
console.log("Navigating to:", comparisonUrl);
|
|
window.location.href = comparisonUrl;
|
|
} else {
|
|
// If no product ID, just go to comparison page
|
|
console.log("No product ID found, navigating to comparison page");
|
|
window.location.href = "product-comparison.html";
|
|
}
|
|
}
|
|
|
|
// Initialize immediately if we're on the product detail page
|
|
if (window.location.pathname.includes("product-detail.html")) {
|
|
console.log(
|
|
"Product detail page detected, initializing compare functionality immediately"
|
|
);
|
|
initProductDetailCompare();
|
|
}
|
|
|
|
// Also try to initialize on DOMContentLoaded
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
console.log("DOMContentLoaded event fired");
|
|
if (window.location.pathname.includes("product-detail.html")) {
|
|
console.log("Product detail page detected in DOMContentLoaded");
|
|
initProductDetailCompare();
|
|
}
|
|
});
|
|
|
|
// Initialize product comparison if on comparison page
|
|
if (window.location.pathname.includes("product-comparison.html")) {
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
initProductComparison();
|
|
});
|
|
}
|
|
|
|
// Initialize comparison functionality if on product catalog page
|
|
if (window.location.pathname.includes("product-catalog.html")) {
|
|
console.log("=== PRODUCT CATALOG PAGE DETECTED ===");
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
console.log(
|
|
"=== DOM CONTENT LOADED - INITIALIZING PRODUCT CATALOG COMPARISON ==="
|
|
);
|
|
initProductCatalogComparison();
|
|
});
|
|
}
|
|
|
|
// Product Catalog - Handle comparison page returns
|
|
function initProductCatalogComparison() {
|
|
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");
|
|
|
|
console.log("Product catalog comparison params:", {
|
|
returnTo,
|
|
slot,
|
|
product1Id,
|
|
product2Id,
|
|
});
|
|
|
|
if (returnTo === "comparison" && slot) {
|
|
console.log("Setting up comparison return functionality for slot:", slot);
|
|
|
|
// Note: Product card click handling is now done in products.js viewProduct method
|
|
console.log(
|
|
"Comparison mode activated - View buttons will navigate to comparison page"
|
|
);
|
|
}
|
|
}
|
|
|
|
// Replace Poppins with Playfair in all font references
|
|
function updateFontClasses() {
|
|
// Find all elements with font-poppins class and replace with font-playfair
|
|
const poppinsElements = document.querySelectorAll(".font-poppins");
|
|
poppinsElements.forEach((element) => {
|
|
element.classList.remove("font-poppins");
|
|
element.classList.add("font-playfair");
|
|
});
|
|
}
|
|
|
|
// Run font update on page load
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
updateFontClasses();
|
|
});
|
|
|
|
// Initialize quote badge on all pages
|
|
function initQuoteBadge() {
|
|
// Load quote items from localStorage
|
|
const storageKey = "khy_quote_items";
|
|
let quoteItems = [];
|
|
|
|
try {
|
|
const stored = localStorage.getItem(storageKey);
|
|
quoteItems = stored ? JSON.parse(stored) : [];
|
|
} catch (error) {
|
|
console.error("Error loading quote items:", error);
|
|
}
|
|
|
|
// Calculate total count
|
|
const count = quoteItems.reduce((total, item) => total + item.quantity, 0);
|
|
|
|
// Update quote badge on all quote links (desktop nav, mobile button, mobile menu)
|
|
const quoteLinks = document.querySelectorAll('a[href="quote.html"]');
|
|
quoteLinks.forEach((quoteLink) => {
|
|
// Remove existing badge
|
|
const existingBadge = quoteLink.querySelector(".quote-badge");
|
|
if (existingBadge) {
|
|
existingBadge.remove();
|
|
}
|
|
|
|
// Add new badge if there are items
|
|
if (count > 0) {
|
|
const badge = document.createElement("span");
|
|
badge.className =
|
|
"quote-badge absolute -top-2 -right-2 bg-uc-gold text-white text-xs rounded-full w-5 h-5 flex items-center justify-center font-semibold";
|
|
badge.textContent = count > 99 ? "99+" : count;
|
|
quoteLink.style.position = "relative";
|
|
quoteLink.appendChild(badge);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize Add To Quote functionality on product detail pages
|
|
function initAddToQuote() {
|
|
const addToQuoteBtn = document.getElementById("add-to-quote-btn");
|
|
if (addToQuoteBtn) {
|
|
try {
|
|
// Ensure button is not treated as a submit in case inside a form
|
|
if (!addToQuoteBtn.getAttribute("type")) {
|
|
addToQuoteBtn.setAttribute("type", "button");
|
|
}
|
|
|
|
addToQuoteBtn.addEventListener("click", function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
console.log("[AddToQuote] Direct click captured");
|
|
|
|
const productData = getProductDataFromPage();
|
|
console.log("[AddToQuote] productData:", productData);
|
|
if (productData) {
|
|
if (typeof addToQuote === "function") {
|
|
addToQuote(productData);
|
|
} else {
|
|
addToQuoteFallback(productData);
|
|
}
|
|
} else {
|
|
console.warn(
|
|
"[AddToQuote] No product data found. Check URL id and DOM."
|
|
);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
console.error("[AddToQuote] Failed to bind direct listener:", err);
|
|
}
|
|
}
|
|
|
|
// Delegated fallback in case the button is replaced dynamically
|
|
document.addEventListener("click", function (e) {
|
|
const btn = e.target.closest && e.target.closest("#add-to-quote-btn");
|
|
if (!btn) return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
console.log("[AddToQuote] Delegated click captured");
|
|
try {
|
|
const productData = getProductDataFromPage();
|
|
console.log("[AddToQuote][delegated] productData:", productData);
|
|
if (productData) {
|
|
if (typeof addToQuote === "function") {
|
|
addToQuote(productData);
|
|
} else {
|
|
addToQuoteFallback(productData);
|
|
}
|
|
} else {
|
|
console.warn("[AddToQuote][delegated] No product data found.");
|
|
}
|
|
} catch (err) {
|
|
console.error("[AddToQuote][delegated] Error handling click:", err);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Get product data from the current page
|
|
function getProductDataFromPage() {
|
|
// Get product ID from URL
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const productId = urlParams.get("id");
|
|
|
|
if (!productId) {
|
|
console.error("No product ID found in URL");
|
|
return null;
|
|
}
|
|
|
|
// Get selected color and size
|
|
const selectedColor = getSelectedColor();
|
|
const selectedSize = getSelectedSize();
|
|
const selectedQuantity = getSelectedQuantity();
|
|
|
|
// Get product name and a best-effort product image
|
|
const productName =
|
|
document.querySelector("h1")?.textContent?.trim() || "Product";
|
|
let productImage = "";
|
|
|
|
// Helper to guard against invalid selectors (e.g., unescaped brackets)
|
|
function qsSafe(selector) {
|
|
try {
|
|
return document.querySelector(selector);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Try a series of selectors safely
|
|
const imageCandidates = [
|
|
".w-\\[500px\\].h-\\[500px\\] img",
|
|
".w-[500px].h-[500px] img",
|
|
".bg-floral-white img",
|
|
"section img[alt]",
|
|
];
|
|
|
|
for (const sel of imageCandidates) {
|
|
const el = qsSafe(sel);
|
|
if (el && el.src) {
|
|
productImage = el.src;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!productImage) {
|
|
const anyImg = qsSafe("img[alt]") || qsSafe("section img") || qsSafe("img");
|
|
productImage = anyImg?.src || "";
|
|
}
|
|
|
|
return {
|
|
id: parseInt(productId),
|
|
name: productName,
|
|
image: productImage,
|
|
color: selectedColor,
|
|
size: selectedSize,
|
|
quantity: selectedQuantity,
|
|
};
|
|
}
|
|
|
|
// Get selected color from the page
|
|
function getSelectedColor() {
|
|
const colorButtons = document.querySelectorAll("button[data-color]");
|
|
for (let button of colorButtons) {
|
|
if (
|
|
button.classList.contains("selected") ||
|
|
button.classList.contains("border-black")
|
|
) {
|
|
return button.getAttribute("data-color") || "Default";
|
|
}
|
|
}
|
|
return "Default";
|
|
}
|
|
|
|
// Get selected size from the page
|
|
function getSelectedSize() {
|
|
const sizeButtons = document.querySelectorAll("button[data-size]");
|
|
for (let button of sizeButtons) {
|
|
if (
|
|
button.classList.contains("selected") ||
|
|
button.classList.contains("bg-uc-gold")
|
|
) {
|
|
return button.getAttribute("data-size") || "Standard";
|
|
}
|
|
}
|
|
return "Standard";
|
|
}
|
|
|
|
// Get selected quantity from the page
|
|
function getSelectedQuantity() {
|
|
const quantitySpan = document.getElementById("qty-value");
|
|
if (quantitySpan) {
|
|
return parseInt(quantitySpan.textContent) || 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Fallback function to add to quote directly
|
|
function addToQuoteFallback(productData) {
|
|
const storageKey = "khy_quote_items";
|
|
let quoteItems = [];
|
|
|
|
try {
|
|
const stored = localStorage.getItem(storageKey);
|
|
quoteItems = stored ? JSON.parse(stored) : [];
|
|
} catch (error) {
|
|
console.error("Error loading quote items:", error);
|
|
quoteItems = [];
|
|
}
|
|
|
|
// Check if item already exists with same specifications
|
|
const existingItemIndex = quoteItems.findIndex(
|
|
(item) =>
|
|
item.id === productData.id &&
|
|
item.color === productData.color &&
|
|
item.size === productData.size
|
|
);
|
|
|
|
if (existingItemIndex !== -1) {
|
|
// Update quantity of existing item
|
|
quoteItems[existingItemIndex].quantity += productData.quantity;
|
|
} else {
|
|
// Add new item
|
|
const newItem = {
|
|
...productData,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
quoteItems.push(newItem);
|
|
}
|
|
|
|
// Save to localStorage
|
|
try {
|
|
localStorage.setItem(storageKey, JSON.stringify(quoteItems));
|
|
|
|
// Update quote badge
|
|
initQuoteBadge();
|
|
|
|
// Show success message
|
|
showAddToQuoteSuccess();
|
|
} catch (error) {
|
|
console.error("Error saving quote items:", error);
|
|
}
|
|
}
|
|
|
|
// Show success message when item is added
|
|
function showAddToQuoteSuccess() {
|
|
// Create success notification
|
|
const notification = document.createElement("div");
|
|
notification.className =
|
|
"fixed top-24 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 translate-x-full";
|
|
notification.innerHTML = `
|
|
<div class="flex items-center space-x-2">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
<span class="font-playfair font-semibold">Added to quote!</span>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Animate in
|
|
setTimeout(() => {
|
|
notification.classList.remove("translate-x-full");
|
|
}, 100);
|
|
|
|
// Remove after 3 seconds
|
|
setTimeout(() => {
|
|
notification.classList.add("translate-x-full");
|
|
setTimeout(() => {
|
|
if (document.body.contains(notification)) {
|
|
document.body.removeChild(notification);
|
|
}
|
|
}, 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// Initialize quote badge immediately if DOM is already loaded
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", initQuoteBadge);
|
|
} else {
|
|
initQuoteBadge();
|
|
}
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", initSite);
|
|
} else {
|
|
initSite();
|
|
}
|
|
|
|
// Initialize quote badge on page load
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
initQuoteBadge();
|
|
initAddToQuote();
|
|
initHeroCarousel();
|
|
initMobileMenu();
|
|
});
|
|
|
|
// Hero Carousel Functionality
|
|
function initHeroCarousel() {
|
|
const arrowButton = document.getElementById("story-arrow-button");
|
|
const heroImage = document.querySelector("#hero-image img");
|
|
|
|
if (!arrowButton || !heroImage) return;
|
|
|
|
// Carousel images array
|
|
const carouselImages = [
|
|
"assets/images/our_story.jpg",
|
|
"assets/images/first_homepage.jpg",
|
|
"assets/images/conference_room.jpg",
|
|
"assets/images/lounge_chair.jpg",
|
|
"assets/images/kitchen.JPG",
|
|
];
|
|
|
|
let currentImageIndex = 0;
|
|
let originalHeight = null;
|
|
|
|
// Function to capture original image height on first load
|
|
function captureOriginalHeight() {
|
|
if (!originalHeight) {
|
|
originalHeight = heroImage.offsetHeight;
|
|
console.log("Original image height captured:", originalHeight + "px");
|
|
}
|
|
}
|
|
|
|
// Function to update image with fade transition
|
|
function updateImage(newIndex) {
|
|
const newImage = new Image();
|
|
newImage.onload = function () {
|
|
// Fade out current image
|
|
heroImage.style.opacity = "0";
|
|
|
|
setTimeout(() => {
|
|
// Change image source
|
|
heroImage.src = carouselImages[newIndex];
|
|
heroImage.alt = `Carousel image ${newIndex + 1}`;
|
|
|
|
// Apply original height to maintain uniformity
|
|
if (originalHeight && newIndex !== 0) {
|
|
heroImage.style.height = originalHeight + "px";
|
|
heroImage.style.objectFit = "cover";
|
|
} else if (newIndex === 0) {
|
|
// Reset to original for the first image
|
|
heroImage.style.height = "auto";
|
|
heroImage.style.objectFit = "initial";
|
|
}
|
|
|
|
// Fade in new image
|
|
heroImage.style.opacity = "1";
|
|
|
|
// Update indicators
|
|
updateIndicators(newIndex);
|
|
}, 300);
|
|
};
|
|
newImage.src = carouselImages[newIndex];
|
|
}
|
|
|
|
// Function to update carousel indicators
|
|
function updateIndicators(activeIndex) {
|
|
for (let i = 0; i < 5; i++) {
|
|
const indicator = document.getElementById(`carousel-indicator-${i}`);
|
|
if (indicator) {
|
|
indicator.style.opacity = i === activeIndex ? "1" : "0.5";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Arrow button click handler
|
|
arrowButton.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
currentImageIndex = (currentImageIndex + 1) % carouselImages.length;
|
|
updateImage(currentImageIndex);
|
|
});
|
|
|
|
// Indicator click handlers
|
|
for (let i = 0; i < 5; i++) {
|
|
const indicator = document.getElementById(`carousel-indicator-${i}`);
|
|
if (indicator) {
|
|
indicator.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
currentImageIndex = i;
|
|
updateImage(currentImageIndex);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Capture original height after image loads
|
|
if (heroImage.complete) {
|
|
captureOriginalHeight();
|
|
} else {
|
|
heroImage.addEventListener("load", captureOriginalHeight);
|
|
}
|
|
|
|
// Auto-advance carousel every 5 seconds
|
|
setInterval(() => {
|
|
currentImageIndex = (currentImageIndex + 1) % carouselImages.length;
|
|
updateImage(currentImageIndex);
|
|
}, 5000);
|
|
}
|
|
|
|
// Mobile Menu Functionality
|
|
function initMobileMenu() {
|
|
const mobileMenuButton = document.getElementById("mobile-menu-button");
|
|
const mobileMenuClose = document.getElementById("mobile-menu-close");
|
|
const mobileMenu = document.getElementById("mobile-menu");
|
|
const mobileMenuOverlay = document.getElementById("mobile-menu-overlay");
|
|
|
|
if (
|
|
!mobileMenuButton ||
|
|
!mobileMenuClose ||
|
|
!mobileMenu ||
|
|
!mobileMenuOverlay
|
|
) {
|
|
console.log("Mobile menu elements not found");
|
|
return;
|
|
}
|
|
|
|
// Function to open mobile menu
|
|
function openMobileMenu() {
|
|
mobileMenu.classList.remove("translate-x-full");
|
|
mobileMenuOverlay.classList.remove("hidden");
|
|
document.body.style.overflow = "hidden"; // Prevent background scrolling
|
|
}
|
|
|
|
// Function to close mobile menu
|
|
function closeMobileMenu() {
|
|
mobileMenu.classList.add("translate-x-full");
|
|
mobileMenuOverlay.classList.add("hidden");
|
|
document.body.style.overflow = ""; // Restore scrolling
|
|
}
|
|
|
|
// Event listeners
|
|
mobileMenuButton.addEventListener("click", openMobileMenu);
|
|
mobileMenuClose.addEventListener("click", closeMobileMenu);
|
|
mobileMenuOverlay.addEventListener("click", closeMobileMenu);
|
|
|
|
// Close menu when clicking on navigation links
|
|
const mobileNavLinks = mobileMenu.querySelectorAll("a");
|
|
mobileNavLinks.forEach((link) => {
|
|
link.addEventListener("click", closeMobileMenu);
|
|
});
|
|
|
|
// Close menu on escape key
|
|
document.addEventListener("keydown", (e) => {
|
|
if (
|
|
e.key === "Escape" &&
|
|
!mobileMenu.classList.contains("translate-x-full")
|
|
) {
|
|
closeMobileMenu();
|
|
}
|
|
});
|
|
|
|
// Handle window resize - close menu if screen becomes large
|
|
window.addEventListener("resize", () => {
|
|
if (window.innerWidth >= 640) {
|
|
// sm breakpoint
|
|
closeMobileMenu();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Version: 4.8 - Added mobile hamburger menu functionality
|