updateweb

This commit is contained in:
Local Server
2025-12-24 00:13:23 -06:00
parent e4b3de4a46
commit 017c6376fc
88 changed files with 17866 additions and 1191 deletions

View File

@@ -1,15 +1,91 @@
// Homepage Editor JavaScript
let homepageData = {};
let quillEditors = {};
let currentMediaPicker = null;
// Initialize Quill editors
function initializeQuillEditors() {
// Check if Quill is loaded
if (typeof Quill === "undefined") {
console.error("Quill.js is not loaded!");
alert("Text editor failed to load. Please refresh the page.");
return;
}
const toolbarOptions = [
["bold", "italic", "underline", "strike"],
["blockquote", "code-block"],
[{ header: 1 }, { header: 2 }],
[{ list: "ordered" }, { list: "bullet" }],
[{ script: "sub" }, { script: "super" }],
[{ indent: "-1" }, { indent: "+1" }],
[{ direction: "rtl" }],
[{ size: ["small", false, "large", "huge"] }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }],
[{ font: [] }],
[{ align: [] }],
["link"],
["clean"],
];
try {
// Initialize Quill for each description field
quillEditors.hero = new Quill("#heroDescription", {
theme: "snow",
modules: { toolbar: toolbarOptions },
placeholder: "Enter hero section description...",
});
quillEditors.promotion = new Quill("#promotionDescription", {
theme: "snow",
modules: { toolbar: toolbarOptions },
placeholder: "Enter promotion description...",
});
quillEditors.portfolio = new Quill("#portfolioDescription", {
theme: "snow",
modules: { toolbar: toolbarOptions },
placeholder: "Enter portfolio description...",
});
console.log("Quill editors initialized successfully");
} catch (error) {
console.error("Error initializing Quill editors:", error);
alert(
"Failed to initialize text editors. Please check the console for errors."
);
}
}
document.addEventListener("DOMContentLoaded", function () {
initializeQuillEditors();
checkAuth().then((authenticated) => {
if (authenticated) {
loadHomepageSettings();
setupMediaLibraryListener();
}
});
});
// Setup media library selection listener
function setupMediaLibraryListener() {
window.addEventListener("message", function (event) {
// Security: verify origin if needed
if (
event.data &&
event.data.type === "mediaSelected" &&
currentMediaPicker
) {
const { section, field } = currentMediaPicker;
handleMediaSelection(section, field, event.data.media);
currentMediaPicker = null;
}
});
}
async function loadHomepageSettings() {
try {
const response = await fetch("/api/admin/homepage/settings", {
@@ -18,14 +94,58 @@ async function loadHomepageSettings() {
const data = await response.json();
if (data.success) {
homepageData = data.settings || {};
// If no data exists, load defaults from the frontend
if (Object.keys(homepageData).length === 0) {
console.log("No homepage data found, loading defaults from frontend");
await loadDefaultsFromFrontend();
}
populateFields();
}
} catch (error) {
console.error("Failed to load homepage settings:", error);
// Load defaults if API fails
await loadDefaultsFromFrontend();
populateFields();
}
}
// Load default content from the current homepage
async function loadDefaultsFromFrontend() {
homepageData = {
hero: {
enabled: true,
headline: "Welcome to Sky Art Shop",
subheading: "Your destination for creative stationery and supplies",
description:
"<p>Discover our curated collection of scrapbooking, journaling, cardmaking, and collaging supplies. Express your creativity and bring your artistic vision to life.</p>",
ctaText: "Shop Now",
ctaLink: "/shop.html",
backgroundUrl: "",
layout: "text-left",
},
promotion: {
enabled: true,
title: "Get Inspired",
description:
"<p>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.</p><p>Explore our collection of washi tapes, stickers, stamps, and more. Each item is carefully selected to help you create something beautiful and meaningful.</p>",
imageUrl: "",
imagePosition: "left",
textAlignment: "left",
},
portfolio: {
enabled: true,
title: "Featured Products",
description: "<p>Discover our most popular items</p>",
count: 6,
},
};
}
function populateFields() {
console.log("Populating fields with data:", homepageData);
// Hero Section
if (homepageData.hero) {
document.getElementById("heroEnabled").checked =
@@ -34,12 +154,32 @@ function populateFields() {
homepageData.hero.headline || "";
document.getElementById("heroSubheading").value =
homepageData.hero.subheading || "";
document.getElementById("heroDescription").value =
homepageData.hero.description || "";
if (homepageData.hero.description) {
quillEditors.hero.root.innerHTML = homepageData.hero.description;
}
document.getElementById("heroCtaText").value =
homepageData.hero.ctaText || "";
document.getElementById("heroCtaLink").value =
homepageData.hero.ctaLink || "";
if (homepageData.hero.backgroundUrl) {
document.getElementById("heroBackgroundUrl").value =
homepageData.hero.backgroundUrl;
displayMediaPreview(
"hero",
"background",
homepageData.hero.backgroundUrl
);
}
if (homepageData.hero.layout) {
const heroSection = document.getElementById("heroSection");
heroSection.setAttribute("data-layout", homepageData.hero.layout);
setActiveButton(`heroSection`, `layout-${homepageData.hero.layout}`);
}
toggleSection("hero");
}
@@ -49,8 +189,46 @@ function populateFields() {
homepageData.promotion.enabled !== false;
document.getElementById("promotionTitle").value =
homepageData.promotion.title || "";
document.getElementById("promotionDescription").value =
homepageData.promotion.description || "";
if (homepageData.promotion.description) {
quillEditors.promotion.root.innerHTML =
homepageData.promotion.description;
}
if (homepageData.promotion.imageUrl) {
document.getElementById("promotionImageUrl").value =
homepageData.promotion.imageUrl;
displayMediaPreview(
"promotion",
"image",
homepageData.promotion.imageUrl
);
}
if (homepageData.promotion.imagePosition) {
const promotionSection = document.getElementById("promotionSection");
promotionSection.setAttribute(
"data-image-position",
homepageData.promotion.imagePosition
);
setActiveButton(
`promotionSection`,
`position-${homepageData.promotion.imagePosition}`
);
}
if (homepageData.promotion.textAlignment) {
const promotionSection = document.getElementById("promotionSection");
promotionSection.setAttribute(
"data-text-alignment",
homepageData.promotion.textAlignment
);
setActiveButton(
`promotionSection`,
`align-${homepageData.promotion.textAlignment}`
);
}
toggleSection("promotion");
}
@@ -60,12 +238,33 @@ function populateFields() {
homepageData.portfolio.enabled !== false;
document.getElementById("portfolioTitle").value =
homepageData.portfolio.title || "";
document.getElementById("portfolioDescription").value =
homepageData.portfolio.description || "";
if (homepageData.portfolio.description) {
quillEditors.portfolio.root.innerHTML =
homepageData.portfolio.description;
}
document.getElementById("portfolioCount").value =
homepageData.portfolio.count || 6;
toggleSection("portfolio");
}
// Show success message
showSuccess(
"Homepage content loaded! You can now edit and preview your changes."
);
}
function setActiveButton(sectionId, className) {
const section = document.getElementById(sectionId);
if (section) {
const buttons = section.querySelectorAll(".alignment-btn");
buttons.forEach((btn) => {
if (btn.classList.contains(className)) {
btn.classList.add("active");
}
});
}
}
function toggleSection(sectionName) {
@@ -75,80 +274,258 @@ function toggleSection(sectionName) {
if (enabled) {
section.classList.remove("disabled");
content
.querySelectorAll("input, textarea, button, select")
.forEach((el) => {
el.disabled = false;
});
content.querySelectorAll("input, button, select").forEach((el) => {
el.disabled = false;
});
// Enable Quill editor
if (quillEditors[sectionName]) {
quillEditors[sectionName].enable();
}
} else {
section.classList.add("disabled");
content
.querySelectorAll("input, textarea, button, select")
.forEach((el) => {
el.disabled = true;
});
content.querySelectorAll("input, button, select").forEach((el) => {
el.disabled = true;
});
// Disable Quill editor
if (quillEditors[sectionName]) {
quillEditors[sectionName].disable();
}
}
}
function previewImage(sectionName) {
const fileInput =
document.getElementById(`${sectionName}Background`) ||
document.getElementById(`${sectionName}Image`);
const preview = document.getElementById(`${sectionName}Preview`);
// Open media library in a modal
function openMediaLibrary(section, field) {
currentMediaPicker = { section, field };
if (fileInput.files && fileInput.files[0]) {
const reader = new FileReader();
reader.onload = function (e) {
preview.classList.remove("empty");
preview.innerHTML = `<img src="${e.target.result}" alt="Preview" />`;
};
reader.readAsDataURL(fileInput.files[0]);
// Create modal backdrop
const backdrop = document.createElement("div");
backdrop.id = "mediaLibraryBackdrop";
backdrop.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 9998;
display: flex;
align-items: center;
justify-content: center;
`;
// Create modal container
const modal = document.createElement("div");
modal.id = "mediaLibraryModal";
modal.style.cssText = `
position: relative;
width: 90%;
max-width: 1200px;
height: 85vh;
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
`;
// Create close button
const closeBtn = document.createElement("button");
closeBtn.innerHTML = '<i class="bi bi-x-lg"></i>';
closeBtn.style.cssText = `
position: absolute;
top: 15px;
right: 15px;
z-index: 10000;
background: #dc3545;
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
`;
closeBtn.onclick = closeMediaLibrary;
// Create iframe
const iframe = document.createElement("iframe");
iframe.id = "mediaLibraryFrame";
iframe.src = "/admin/media-library.html?selectMode=true";
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
`;
// Setup iframe message listener
iframe.onload = function () {
iframe.contentWindow.postMessage(
{
type: "initSelectMode",
section: section,
field: field,
},
"*"
);
};
modal.appendChild(closeBtn);
modal.appendChild(iframe);
backdrop.appendChild(modal);
document.body.appendChild(backdrop);
// Close on backdrop click
backdrop.onclick = function (e) {
if (e.target === backdrop) {
closeMediaLibrary();
}
};
}
function closeMediaLibrary() {
const backdrop = document.getElementById("mediaLibraryBackdrop");
if (backdrop) {
backdrop.remove();
}
currentMediaPicker = null;
}
function handleMediaSelection(section, field, media) {
closeMediaLibrary();
const urlField = document.getElementById(
`${section}${field === "background" ? "Background" : "Image"}Url`
);
if (urlField) {
urlField.value = media.url;
}
displayMediaPreview(section, field, media.url);
showSuccess(`Media selected successfully!`);
}
function displayMediaPreview(section, field, url) {
const previewId = `${section}Preview`;
const preview = document.getElementById(previewId);
const clearBtnId = `${section}${
field === "background" ? "Background" : "Image"
}Clear`;
const clearBtn = document.getElementById(clearBtnId);
if (preview) {
preview.classList.remove("empty");
// Check if it's a video
const isVideo = url.match(/\.(mp4|webm|ogg)$/i);
if (isVideo) {
preview.innerHTML = `<video src="${url}" style="max-width: 100%; max-height: 100%;" controls></video>`;
} else {
preview.innerHTML = `<img src="${url}" alt="Preview" />`;
}
}
if (clearBtn) {
clearBtn.style.display = "inline-block";
}
}
function clearMedia(section, field) {
const urlField = document.getElementById(
`${section}${field === "background" ? "Background" : "Image"}Url`
);
if (urlField) {
urlField.value = "";
}
const previewId = `${section}Preview`;
const preview = document.getElementById(previewId);
if (preview) {
preview.classList.add("empty");
preview.innerHTML = '<i class="bi bi-image" style="font-size: 3rem"></i>';
}
const clearBtnId = `${section}${
field === "background" ? "Background" : "Image"
}Clear`;
const clearBtn = document.getElementById(clearBtnId);
if (clearBtn) {
clearBtn.style.display = "none";
}
showSuccess("Media cleared");
}
function setLayout(sectionName, layout) {
const buttons = document.querySelectorAll(
`#${sectionName}Section .alignment-btn`
);
const section = document.getElementById(`${sectionName}Section`);
const buttons = section.querySelectorAll(".alignment-btn");
buttons.forEach((btn) => btn.classList.remove("active"));
event.target.closest(".alignment-btn").classList.add("active");
// Store in a data attribute
section.setAttribute(`data-layout`, layout);
}
function setImagePosition(sectionName, position) {
const section = document.getElementById(`${sectionName}Section`);
const buttons = event.target
.closest(".alignment-selector")
.querySelectorAll(".alignment-btn");
buttons.forEach((btn) => btn.classList.remove("active"));
event.target.closest(".alignment-btn").classList.add("active");
section.setAttribute(`data-image-position`, position);
}
function setTextAlignment(sectionName, alignment) {
const section = document.getElementById(`${sectionName}Section`);
const buttons = event.target
.closest(".alignment-selector")
.querySelectorAll(".alignment-btn");
buttons.forEach((btn) => btn.classList.remove("active"));
event.target.closest(".alignment-btn").classList.add("active");
section.setAttribute(`data-text-alignment`, alignment);
}
async function saveHomepage() {
// Get hero layout
const heroSection = document.getElementById("heroSection");
const heroLayout = heroSection.getAttribute("data-layout") || "text-left";
// Get promotion layout settings
const promotionSection = document.getElementById("promotionSection");
const promotionImagePosition =
promotionSection.getAttribute("data-image-position") || "left";
const promotionTextAlignment =
promotionSection.getAttribute("data-text-alignment") || "left";
const settings = {
hero: {
enabled: document.getElementById("heroEnabled").checked,
headline: document.getElementById("heroHeadline").value,
subheading: document.getElementById("heroSubheading").value,
description: document.getElementById("heroDescription").value,
description: quillEditors.hero.root.innerHTML,
ctaText: document.getElementById("heroCtaText").value,
ctaLink: document.getElementById("heroCtaLink").value,
backgroundUrl: document.getElementById("heroBackgroundUrl")?.value || "",
layout: heroLayout,
},
promotion: {
enabled: document.getElementById("promotionEnabled").checked,
title: document.getElementById("promotionTitle").value,
description: document.getElementById("promotionDescription").value,
description: quillEditors.promotion.root.innerHTML,
imageUrl: document.getElementById("promotionImageUrl")?.value || "",
imagePosition: promotionImagePosition,
textAlignment: promotionTextAlignment,
},
portfolio: {
enabled: document.getElementById("portfolioEnabled").checked,
title: document.getElementById("portfolioTitle").value,
description: document.getElementById("portfolioDescription").value,
description: quillEditors.portfolio.root.innerHTML,
count: parseInt(document.getElementById("portfolioCount").value) || 6,
},
};
@@ -164,8 +541,9 @@ async function saveHomepage() {
const data = await response.json();
if (data.success) {
showSuccess(
"Homepage settings saved successfully! Changes are now live."
"Homepage settings saved successfully! Changes are now live on the frontend."
);
homepageData = settings;
} else {
showError(data.message || "Failed to save homepage settings");
}
@@ -175,22 +553,30 @@ async function saveHomepage() {
}
}
async function logout() {
try {
const response = await fetch("/api/admin/logout", {
method: "POST",
credentials: "include",
});
if (response.ok) window.location.href = "/admin/login.html";
} catch (error) {
console.error("Logout failed:", error);
}
}
function showSuccess(message) {
alert(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}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
}
function showError(message) {
alert("Error: " + 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}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
}