// Homepage Editor JavaScript - Updated for Hero Slides let homepageData = { heroSlides: [], featuredProducts: { enabled: true, title: "Featured Products", count: 4, }, blog: { enabled: true, title: "Get Inspired", count: 3, }, about: { enabled: true, title: "About Sky Art Shop", description: "At Sky Art Shop, we believe in the power of creativity to transform and inspire. Whether you're an experienced crafter or just beginning your creative journey, we have everything you need to bring your ideas to life.", imageUrl: "", }, }; let currentMediaTarget = null; let mediaLibrary = null; // Quill editors storage let quillEditors = {}; // Quill toolbar configuration const quillToolbarOptions = [ ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }], [{ header: [1, 2, 3, false] }], [{ color: [] }, { background: [] }], [{ align: [] }], ["link"], ["clean"], ]; // Initialize on DOM load document.addEventListener("DOMContentLoaded", function () { // Initialize the modern media library mediaLibrary = new MediaLibrary({ selectMode: true, multiple: false, onSelect: handleMediaSelection, }); checkAuth().then((authenticated) => { if (authenticated) { loadHomepageSettings(); } }); }); // Load homepage settings from backend async function loadHomepageSettings() { try { const response = await fetch("/api/admin/homepage/settings", { credentials: "include", }); const data = await response.json(); if (data.success && data.settings) { // Migrate old format to new format if needed if (data.settings.hero && !data.settings.heroSlides) { // Convert old single hero to slides homepageData.heroSlides = [ { id: generateSlideId(), title: data.settings.hero.headline || "Welcome to Sky Art Shop", description: stripHtml(data.settings.hero.description) || "Discover our beautiful collection of stationery and crafting supplies.", buttonText: data.settings.hero.ctaText || "Shop Now", buttonLink: data.settings.hero.ctaLink || "/shop", imageUrl: data.settings.hero.backgroundUrl || "", }, ]; } else if (data.settings.heroSlides) { homepageData.heroSlides = data.settings.heroSlides; } // Load featured products settings if (data.settings.featuredProducts) { homepageData.featuredProducts = data.settings.featuredProducts; } // Load blog settings if (data.settings.blog) { homepageData.blog = data.settings.blog; } // Load about settings if (data.settings.about) { homepageData.about = data.settings.about; } } // Initialize with default slides if empty if (homepageData.heroSlides.length === 0) { homepageData.heroSlides = getDefaultSlides(); } renderAllSections(); showSuccess("Homepage settings loaded successfully!"); } catch (error) { console.error("Failed to load homepage settings:", error); // Load defaults homepageData.heroSlides = getDefaultSlides(); renderAllSections(); } } // Get default slides function getDefaultSlides() { return [ { id: generateSlideId(), title: "Welcome to Sky Art Shop", description: "Discover our beautiful collection of stationery, washi tapes, stickers, and crafting supplies. Perfect for bullet journaling, scrapbooking, and all your creative projects.", buttonText: "Shop Now", buttonLink: "/shop", imageUrl: "https://images.unsplash.com/photo-1513519245088-0e12902e35a6?w=1920&q=80", }, { id: generateSlideId(), title: "Express Your Creativity", description: "From vibrant markers to elegant journals, find everything you need to bring your artistic vision to life. Quality supplies for every crafter.", buttonText: "Explore Collection", buttonLink: "/shop", imageUrl: "https://images.unsplash.com/photo-1456735190827-d1262f71b8a3?w=1920&q=80", }, { id: generateSlideId(), title: "New Arrivals Every Week", description: "Stay inspired with our constantly updated selection of unique and trendy stationery. Be the first to discover our latest additions.", buttonText: "See What's New", buttonLink: "/shop", imageUrl: "https://images.unsplash.com/photo-1452860606245-08befc0ff44b?w=1920&q=80", }, ]; } // Strip HTML tags from text function stripHtml(html) { if (!html) return ""; const tmp = document.createElement("DIV"); tmp.innerHTML = html; return tmp.textContent || tmp.innerText || ""; } // Generate unique slide ID function generateSlideId() { return "slide_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9); } // Render all sections function renderAllSections() { renderSlides(); renderFeaturedProducts(); renderBlogSection(); renderAboutSection(); } // Render hero slides function renderSlides() { const container = document.getElementById("slidesContainer"); const countBadge = document.getElementById("slideCount"); // Destroy existing Quill editors for slides Object.keys(quillEditors).forEach((key) => { if (key.startsWith("slide_desc_")) { delete quillEditors[key]; } }); countBadge.textContent = `${homepageData.heroSlides.length} slides`; if (homepageData.heroSlides.length === 0) { container.innerHTML = '

No slides yet. Click "Add New Slide" to create one.

'; return; } container.innerHTML = homepageData.heroSlides .map( (slide, index) => `
Slide ${index + 1}
${ index > 0 ? `` : "" } ${ index < homepageData.heroSlides.length - 1 ? `` : "" }
Recommended: 1920 x 600 px (landscape)
${ slide.imageUrl ? `Slide preview` : '' }
${ slide.imageUrl ? ` ` : "" }
` ) .join(""); // Initialize Quill editors for each slide description setTimeout(() => { homepageData.heroSlides.forEach((slide) => { initSlideQuillEditor(slide.id, slide.description); }); }, 100); } // Add new slide function addNewSlide() { const newSlide = { id: generateSlideId(), title: "", description: "", buttonText: "Shop Now", buttonLink: "/shop", imageUrl: "", }; homepageData.heroSlides.push(newSlide); renderSlides(); // Scroll to the new slide setTimeout(() => { const newSlideEl = document.querySelector( `[data-slide-id="${newSlide.id}"]` ); if (newSlideEl) { newSlideEl.scrollIntoView({ behavior: "smooth", block: "center" }); newSlideEl.classList.add("active"); setTimeout(() => newSlideEl.classList.remove("active"), 2000); } }, 100); } // Update slide field function updateSlide(slideId, field, value) { const slide = homepageData.heroSlides.find((s) => s.id === slideId); if (slide) { slide[field] = value; } } // Update slide image function updateSlideImage(slideId, url) { const slide = homepageData.heroSlides.find((s) => s.id === slideId); if (slide) { slide.imageUrl = url; const preview = document.getElementById(`slidePreview_${slideId}`); if (preview) { if (url) { preview.classList.remove("empty"); preview.innerHTML = `Slide preview`; } else { preview.classList.add("empty"); preview.innerHTML = ''; } } } } // Clear slide image function clearSlideImage(slideId) { updateSlide(slideId, "imageUrl", ""); document.getElementById(`slideImage_${slideId}`).value = ""; renderSlides(); } // Move slide up or down function moveSlide(slideId, direction) { const index = homepageData.heroSlides.findIndex((s) => s.id === slideId); if (index === -1) return; const newIndex = index + direction; if (newIndex < 0 || newIndex >= homepageData.heroSlides.length) return; // Swap const temp = homepageData.heroSlides[index]; homepageData.heroSlides[index] = homepageData.heroSlides[newIndex]; homepageData.heroSlides[newIndex] = temp; renderSlides(); } // Delete slide function deleteSlide(slideId) { if (homepageData.heroSlides.length <= 1) { showError("You must have at least one slide"); return; } showDeleteConfirm( "Are you sure you want to delete this slide?", () => { homepageData.heroSlides = homepageData.heroSlides.filter( (s) => s.id !== slideId ); renderSlides(); showSuccess("Slide deleted"); }, { title: "Delete Slide", confirmText: "Delete Slide" } ); } // Render featured products section function renderFeaturedProducts() { document.getElementById("featuredEnabled").checked = homepageData.featuredProducts.enabled; document.getElementById("featuredTitle").value = homepageData.featuredProducts.title || "Featured Products"; // Set active count button document .querySelectorAll("#featuredProductsSection .count-btn") .forEach((btn) => { btn.classList.toggle( "active", parseInt(btn.dataset.count) === homepageData.featuredProducts.count ); }); } // Set featured products count function setFeaturedCount(count) { homepageData.featuredProducts.count = count; document .querySelectorAll("#featuredProductsSection .count-btn") .forEach((btn) => { btn.classList.toggle("active", parseInt(btn.dataset.count) === count); }); } // Render blog section function renderBlogSection() { document.getElementById("blogEnabled").checked = homepageData.blog.enabled; document.getElementById("blogTitle").value = homepageData.blog.title || "Get Inspired"; // Set active count button document.querySelectorAll("#blogSection .count-btn").forEach((btn) => { btn.classList.toggle( "active", parseInt(btn.dataset.count) === homepageData.blog.count ); }); } // Set blog posts count function setBlogCount(count) { homepageData.blog.count = count; document.querySelectorAll("#blogSection .count-btn").forEach((btn) => { btn.classList.toggle("active", parseInt(btn.dataset.count) === count); }); } // Render about section function renderAboutSection() { document.getElementById("aboutEnabled").checked = homepageData.about.enabled; document.getElementById("aboutTitle").value = homepageData.about.title || "About Sky Art Shop"; // Initialize Quill for about description initAboutQuillEditor(homepageData.about.description || ""); const preview = document.getElementById("aboutPreview"); if (homepageData.about.imageUrl) { document.getElementById("aboutImageUrl").value = homepageData.about.imageUrl; preview.classList.remove("empty"); preview.innerHTML = `About preview`; } } // Initialize Quill editor for About section function initAboutQuillEditor(content) { const editorEl = document.getElementById("aboutDescriptionEditor"); if (!editorEl) return; // Destroy existing editor if any if (quillEditors["about"]) { delete quillEditors["about"]; } quillEditors["about"] = new Quill("#aboutDescriptionEditor", { theme: "snow", modules: { toolbar: quillToolbarOptions, }, placeholder: "Write a short description about your shop...", }); // Set initial content if (content) { quillEditors["about"].root.innerHTML = content; } // Update data on change quillEditors["about"].on("text-change", function () { homepageData.about.description = quillEditors["about"].root.innerHTML; }); } // Initialize Quill editor for slide description function initSlideQuillEditor(slideId, content) { const editorEl = document.getElementById(`slideDesc_${slideId}`); if (!editorEl) return; const editorKey = `slide_desc_${slideId}`; quillEditors[editorKey] = new Quill(`#slideDesc_${slideId}`, { theme: "snow", modules: { toolbar: [ ["bold", "italic", "underline"], [{ list: "ordered" }, { list: "bullet" }], ["link"], ["clean"], ], }, placeholder: "Enter slide description...", }); // Set initial content if (content) { quillEditors[editorKey].root.innerHTML = content; } // Update slide data on change quillEditors[editorKey].on("text-change", function () { const slide = homepageData.heroSlides.find((s) => s.id === slideId); if (slide) { slide.description = quillEditors[editorKey].root.innerHTML; } }); } // Open media library function openMediaLibrary(type, id = null) { currentMediaTarget = { type, id }; mediaLibrary.open(); } // Handle media selection function handleMediaSelection(media) { if (!currentMediaTarget) return; // Media is now a single object with path property if (!media || !media.path) { showError("No image selected"); return; } console.log("Selected media:", media); const { type, id } = currentMediaTarget; if (type === "slide" && id) { const slide = homepageData.heroSlides.find((s) => s.id === id); if (slide) { slide.imageUrl = media.path; const hiddenInput = document.getElementById(`slideImage_${id}`); if (hiddenInput) { hiddenInput.value = media.path; } const preview = document.getElementById(`slidePreview_${id}`); if (preview) { preview.classList.remove("empty"); preview.innerHTML = `Slide preview`; } console.log("Updated slide:", slide); // Don't re-render, just update the preview and data } } else if (type === "about") { homepageData.about.imageUrl = media.path; document.getElementById("aboutImageUrl").value = media.path; const preview = document.getElementById("aboutPreview"); preview.classList.remove("empty"); preview.innerHTML = `About preview`; } currentMediaTarget = null; showSuccess("Image selected successfully!"); } // Escape HTML function escapeHtml(text) { if (!text) return ""; const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } // Save all homepage settings async function saveHomepage() { // Collect all data from form homepageData.featuredProducts = { enabled: document.getElementById("featuredEnabled").checked, title: document.getElementById("featuredTitle").value, count: homepageData.featuredProducts.count, }; homepageData.blog = { enabled: document.getElementById("blogEnabled").checked, title: document.getElementById("blogTitle").value, count: homepageData.blog.count, }; // Get description from Quill editor const aboutDescription = quillEditors["about"] ? quillEditors["about"].root.innerHTML : homepageData.about.description; homepageData.about = { enabled: document.getElementById("aboutEnabled").checked, title: document.getElementById("aboutTitle").value, description: aboutDescription, imageUrl: document.getElementById("aboutImageUrl").value, }; // Update slide descriptions from Quill editors homepageData.heroSlides.forEach((slide) => { const editorKey = `slide_desc_${slide.id}`; if (quillEditors[editorKey]) { slide.description = quillEditors[editorKey].root.innerHTML; } }); // Validate slides for (const slide of homepageData.heroSlides) { if (!slide.title) { showError("All slides must have a title"); return; } } try { const response = await fetch("/api/admin/homepage/settings", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify(homepageData), }); const data = await response.json(); if (data.success) { showSuccess( "Homepage settings saved successfully! Changes are now live on the frontend." ); } else { showError(data.message || "Failed to save homepage settings"); } } catch (error) { console.error("Failed to save homepage:", error); showError("Failed to save homepage settings"); } } // Show success notification function showSuccess(message) { const alert = document.createElement("div"); alert.className = "alert alert-success alert-dismissible fade show position-fixed"; alert.style.cssText = "top: 20px; right: 20px; z-index: 9999; min-width: 300px;"; alert.innerHTML = ` ${message} `; document.body.appendChild(alert); setTimeout(() => alert.remove(), 5000); } // Show error notification function showError(message) { const alert = document.createElement("div"); alert.className = "alert alert-danger alert-dismissible fade show position-fixed"; alert.style.cssText = "top: 20px; right: 20px; z-index: 9999; min-width: 300px;"; alert.innerHTML = ` ${message} `; document.body.appendChild(alert); setTimeout(() => alert.remove(), 5000); }