// Pages Management JavaScript let pagesData = []; let pageModal; let quillEditor; let aboutContentEditor; let aboutTeamMembers = []; let deletedTeamMemberIds = []; // Track deleted team members for database deletion let currentMediaPicker = null; let pagesMediaLibrary = null; // Initialize pages media library function initPagesMediaLibrary() { if (typeof MediaLibrary !== "undefined" && !pagesMediaLibrary) { pagesMediaLibrary = new MediaLibrary({ selectMode: true, multiple: false, onSelect: function (media) { handleMediaSelection(media); }, }); } } document.addEventListener("DOMContentLoaded", function () { pageModal = new bootstrap.Modal(document.getElementById("pageModal")); initializeQuillEditor(); initializeAboutEditor(); initPagesMediaLibrary(); checkAuth().then((authenticated) => { if (authenticated) { loadPages(); } }); const urlParams = new URLSearchParams(window.location.search); if (urlParams.get("action") === "create") { showCreatePage(); } // Auto-generate slug from title document.getElementById("pageTitle")?.addEventListener("input", function () { if (!document.getElementById("pageId").value) { document.getElementById("pageSlug").value = slugify(this.value); } }); }); function initializeQuillEditor() { quillEditor = new Quill("#pageContentEditor", { theme: "snow", modules: { toolbar: [ [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ font: [] }], [{ size: ["small", false, "large", "huge"] }], ["bold", "italic", "underline", "strike"], [{ color: [] }, { background: [] }], [{ script: "sub" }, { script: "super" }], [{ list: "ordered" }, { list: "bullet" }], [{ indent: "-1" }, { indent: "+1" }], [{ align: [] }], ["blockquote", "code-block"], ["link", "image", "video"], ["clean"], ], }, }); // Custom image handler to use media library const toolbar = quillEditor.getModule("toolbar"); toolbar.addHandler("image", function () { openMediaLibraryForPageEditor(); }); } // Open media library for main page editor image insertion function openMediaLibraryForPageEditor() { if (!pagesMediaLibrary) { initPagesMediaLibrary(); } currentMediaPicker = "pageEditorImage"; pagesMediaLibrary.show(); } // Handle media selection for main page editor function handlePageEditorImageSelection(media) { if (quillEditor && media && media.path) { const range = quillEditor.getSelection(true); quillEditor.insertEmbed(range.index, "image", media.path); quillEditor.setSelection(range.index + 1); } } function initializeAboutEditor() { aboutContentEditor = new Quill("#aboutContentEditor", { theme: "snow", modules: { toolbar: [ [{ header: [1, 2, 3, false] }], ["bold", "italic", "underline"], [{ color: [] }, { background: [] }], [{ list: "ordered" }, { list: "bullet" }], [{ align: [] }], ["link", "image"], ["clean"], ], }, placeholder: "Write your page content here...", }); // Custom image handler to use media library const toolbar = aboutContentEditor.getModule("toolbar"); toolbar.addHandler("image", function () { openMediaLibraryForAboutEditor(); }); } // Open media library for About editor image insertion function openMediaLibraryForAboutEditor() { if (!pagesMediaLibrary) { initPagesMediaLibrary(); } currentMediaPicker = "aboutEditorImage"; pagesMediaLibrary.show(); } // Handle media selection for About editor function handleAboutEditorImageSelection(media) { if (aboutContentEditor && media && media.path) { const range = aboutContentEditor.getSelection(true); aboutContentEditor.insertEmbed(range.index, "image", media.path); aboutContentEditor.setSelection(range.index + 1); } } async function loadPages() { try { const response = await fetch("/api/admin/pages", { credentials: "include", }); const data = await response.json(); if (data.success) { pagesData = data.pages; renderPages(pagesData); } } catch (error) { console.error("Failed to load pages:", error); } } function renderPages(pages) { const tbody = document.getElementById("pagesTableBody"); if (pages.length === 0) { tbody.innerHTML = `

No custom pages found

`; return; } tbody.innerHTML = pages .map( (p) => ` ${escapeHtml(p.id)} ${escapeHtml(p.title)} ${escapeHtml(p.slug)} ${p.ispublished ? "Published" : "Draft"} ${formatDate(p.createdat)} `, ) .join(""); } function filterPages() { const searchTerm = document.getElementById("searchInput").value.toLowerCase(); const filtered = pagesData.filter( (p) => p.title.toLowerCase().includes(searchTerm) || p.slug.toLowerCase().includes(searchTerm), ); renderPages(filtered); } function showCreatePage() { document.getElementById("modalTitle").textContent = "Create Custom Page"; document.getElementById("pageForm").reset(); document.getElementById("pageId").value = ""; document.getElementById("pagePublished").checked = true; quillEditor.setContents([]); // Show regular editor by default document.getElementById("contactStructuredFields").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "none"; document.getElementById("privacyContentSection").style.display = "none"; document.getElementById("regularContentEditor").style.display = "block"; pageModal.show(); } async function editPage(id) { try { const response = await fetch(`/api/admin/pages/${id}`, { credentials: "include", }); const data = await response.json(); if (data.success) { const page = data.page; document.getElementById("modalTitle").textContent = "Edit Custom Page"; document.getElementById("pageId").value = page.id; document.getElementById("pageTitle").value = page.title; document.getElementById("pageSlug").value = page.slug; // Check if this is the contact page - use structured fields if (page.slug === "contact" || page.slug === "page-contact") { if (page.pagedata) { showContactStructuredFields(page.pagedata); } else { // Contact page without pagedata, use regular editor document.getElementById("contactStructuredFields").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "none"; document.getElementById("regularContentEditor").style.display = "block"; if (page.content) { try { const delta = JSON.parse(page.content); quillEditor.setContents(delta); } catch { quillEditor.clipboard.dangerouslyPasteHTML(page.content); } } else { quillEditor.setContents([]); } } } else if ( page.slug === "about" || page.slug === "page-about" || page.slug.includes("about") ) { // Show About page with team members await showAboutWithTeamFields(page); } else if ( page.slug === "privacy" || page.slug === "page-privacy" || page.slug.includes("privacy") || page.slug === "shipping" || page.slug === "shipping-info" || page.slug.includes("shipping") || page.slug === "returns" || page.slug.includes("return") ) { // Show Privacy/Shipping/Returns page with structured fields if (page.pagedata) { showPrivacyStructuredFields(page.pagedata); } else { // No pagedata, use regular editor document.getElementById("contactStructuredFields").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "none"; document.getElementById("privacyContentSection").style.display = "none"; document.getElementById("regularContentEditor").style.display = "block"; if (page.content) { try { const delta = JSON.parse(page.content); quillEditor.setContents(delta); } catch { quillEditor.clipboard.dangerouslyPasteHTML(page.content); } } else { quillEditor.setContents([]); } } } else { // Use regular Quill editor for all other pages (privacy, etc) document.getElementById("contactStructuredFields").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "none"; document.getElementById("regularContentEditor").style.display = "block"; // Set Quill editor content if (page.content) { try { const delta = JSON.parse(page.content); quillEditor.setContents(delta); } catch { // If content is plain HTML/text, set it directly quillEditor.clipboard.dangerouslyPasteHTML(page.content); } } else { quillEditor.setContents([]); } } document.getElementById("pageMetaTitle").value = page.metatitle || ""; document.getElementById("pageMetaDescription").value = page.metadescription || ""; document.getElementById("pagePublished").checked = page.ispublished; pageModal.show(); } } catch (error) { console.error("Failed to load page:", error); showError("Failed to load page details"); } } function showContactStructuredFields(pagedata) { // Hide regular editor, show structured fields document.getElementById("regularContentEditor").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "none"; document.getElementById("contactStructuredFields").style.display = "block"; // Populate header fields if (pagedata.header) { document.getElementById("contactHeaderTitle").value = pagedata.header.title || ""; document.getElementById("contactHeaderSubtitle").value = pagedata.header.subtitle || ""; } // Populate contact info if (pagedata.contactInfo) { document.getElementById("contactPhone").value = pagedata.contactInfo.phone || ""; document.getElementById("contactEmail").value = pagedata.contactInfo.email || ""; document.getElementById("contactAddress").value = pagedata.contactInfo.address || ""; } // Populate business hours if (pagedata.businessHours) { renderBusinessHours(pagedata.businessHours); } } function renderBusinessHours(hours) { const container = document.getElementById("businessHoursList"); container.innerHTML = hours .map( (hour, index) => `
`, ) .join(""); } function addBusinessHour() { const container = document.getElementById("businessHoursList"); const index = container.children.length; const newHour = document.createElement("div"); newHour.className = "row mb-2"; newHour.setAttribute("data-hour-index", index); newHour.innerHTML = `
`; container.appendChild(newHour); } function removeBusinessHour(index) { const container = document.getElementById("businessHoursList"); const hourRow = container.querySelector(`[data-hour-index="${index}"]`); if (hourRow) { hourRow.remove(); } } // Privacy Policy Page Functions let privacySectionEditors = []; // Array to store Quill editors for each section function showPrivacyStructuredFields(pagedata) { // Hide regular editor, show privacy fields document.getElementById("regularContentEditor").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "none"; document.getElementById("contactStructuredFields").style.display = "none"; document.getElementById("privacyContentSection").style.display = "block"; // Populate header fields if (pagedata.header) { document.getElementById("privacyHeaderTitle").value = pagedata.header.title || ""; } // Populate last updated document.getElementById("privacyLastUpdated").value = pagedata.lastUpdated || ""; // Populate sections if (pagedata.sections && pagedata.sections.length > 0) { renderPrivacySections(pagedata.sections); } else { // Start with one empty section privacySectionEditors = []; document.getElementById("privacySectionsContainer").innerHTML = ""; } } function renderPrivacySections(sections) { const container = document.getElementById("privacySectionsContainer"); privacySectionEditors = []; // Reset editors array container.innerHTML = ""; sections.forEach((section, index) => { addPrivacySectionWithData(section, index); }); } function addPrivacySection() { addPrivacySectionWithData( { title: "", content: "" }, privacySectionEditors.length, ); } function addPrivacySectionWithData(section, index) { const container = document.getElementById("privacySectionsContainer"); const sectionDiv = document.createElement("div"); sectionDiv.className = "contact-field-group mb-4"; sectionDiv.setAttribute("data-section-index", index); // Create unique IDs for this section's editor const editorId = `privacySectionContent_${index}`; sectionDiv.innerHTML = `
Section ${index + 1}
`; container.appendChild(sectionDiv); // Initialize Quill editor for this section's content const editor = new Quill(`#${editorId}`, { theme: "snow", modules: { toolbar: [ [{ header: [2, 3, false] }], ["bold", "italic", "underline"], [{ list: "ordered" }, { list: "bullet" }], ["link"], ["clean"], ], }, }); // Set the content if provided if (section.content) { try { const delta = JSON.parse(section.content); editor.setContents(delta); } catch { // If it's plain text/HTML, set it directly editor.clipboard.dangerouslyPasteHTML(section.content); } } privacySectionEditors[index] = editor; } function removePrivacySection(index) { const container = document.getElementById("privacySectionsContainer"); const sectionDiv = container.querySelector(`[data-section-index="${index}"]`); if (sectionDiv) { sectionDiv.remove(); // Remove editor from array (set to null to keep indices) privacySectionEditors[index] = null; } } // About Page with Team Members Functions async function showAboutWithTeamFields(page) { // Hide other editors document.getElementById("contactStructuredFields").style.display = "none"; document.getElementById("regularContentEditor").style.display = "none"; document.getElementById("aboutWithTeamFields").style.display = "block"; // Set About content if (page.content) { try { const delta = JSON.parse(page.content); aboutContentEditor.setContents(delta); } catch { aboutContentEditor.clipboard.dangerouslyPasteHTML(page.content); } } else { aboutContentEditor.setContents([]); } // Load team members from database await loadTeamMembersForAbout(); } async function loadTeamMembersForAbout() { try { // Reset the deleted IDs when loading fresh data deletedTeamMemberIds = []; const response = await fetch("/api/admin/team-members", { credentials: "include", }); const data = await response.json(); if (data.success && data.teamMembers) { aboutTeamMembers = data.teamMembers; displayTeamMembersInEditor(); } } catch (error) { console.error("Error loading team members:", error); } } function displayTeamMembersInEditor() { const container = document.getElementById("teamMembersList"); if (aboutTeamMembers.length === 0) { container.innerHTML = `

No team members yet. Click "Add Member" to get started.

`; return; } container.innerHTML = aboutTeamMembers .map( (member, index) => `
${ member.image_url ? `${escapeHtml(
                  member.name,
                )}` : `` }
`, ) .join(""); } function addTeamMember() { const newMember = { id: null, name: "", position: "", bio: "", image_url: "", display_order: aboutTeamMembers.length, }; aboutTeamMembers.push(newMember); displayTeamMembersInEditor(); } function updateTeamMember(index, field, value) { if (aboutTeamMembers[index]) { aboutTeamMembers[index][field] = value; } } function removeTeamMemberFromAbout(index) { const member = aboutTeamMembers[index]; // If member has an ID, track it for deletion from database if (member && member.id) { deletedTeamMemberIds.push(member.id); } aboutTeamMembers.splice(index, 1); displayTeamMembersInEditor(); } function selectImageForMember(index) { currentMediaPicker = { purpose: "teamMember", index }; // Initialize if not already initPagesMediaLibrary(); if (pagesMediaLibrary) { pagesMediaLibrary.open(); } } function handleMediaSelection(media) { if (!currentMediaPicker) return; // Handle About editor image insertion if (currentMediaPicker === "aboutEditorImage") { handleAboutEditorImageSelection(media); currentMediaPicker = null; return; } // Handle main page editor image insertion if (currentMediaPicker === "pageEditorImage") { handlePageEditorImageSelection(media); currentMediaPicker = null; return; } if (currentMediaPicker.purpose === "teamMember") { const index = currentMediaPicker.index; if (aboutTeamMembers[index]) { aboutTeamMembers[index].image_url = media.path; displayTeamMembersInEditor(); } } currentMediaPicker = null; } async function saveTeamMembers() { try { // First, delete any removed team members from the database for (const memberId of deletedTeamMemberIds) { try { await fetch(`/api/admin/team-members/${memberId}`, { method: "DELETE", credentials: "include", }); console.log(`Deleted team member ${memberId}`); } catch (err) { console.error(`Failed to delete team member ${memberId}:`, err); } } // Clear the deleted IDs array after processing deletedTeamMemberIds = []; // Save or update each team member for (const member of aboutTeamMembers) { if (!member.name || !member.position) continue; // Skip incomplete members const payload = { name: member.name, position: member.position, bio: member.bio || "", image_url: member.image_url || "", display_order: parseInt(member.display_order) || 0, }; if (member.id) { // Update existing await fetch(`/api/admin/team-members/${member.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify(payload), }); } else { // Create new const response = await fetch("/api/admin/team-members", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify(payload), }); const data = await response.json(); if (data.success && data.teamMember) { member.id = data.teamMember.id; // Update with new ID } } } console.log("Team members saved successfully"); } catch (error) { console.error("Error saving team members:", error); } } async function savePage() { const id = document.getElementById("pageId").value; const slug = document.getElementById("pageSlug").value; let formData = { title: document.getElementById("pageTitle").value, slug: slug, metatitle: document.getElementById("pageMetaTitle").value, metadescription: document.getElementById("pageMetaDescription").value, ispublished: document.getElementById("pagePublished").checked, }; // Check if this is About page with team members if ( (slug === "about" || slug === "page-about") && document.getElementById("aboutWithTeamFields").style.display !== "none" ) { // Get About content from editor const contentDelta = aboutContentEditor.getContents(); formData.content = JSON.stringify(contentDelta); // Save team members separately await saveTeamMembers(); } // Check if this is contact page with structured fields visible else if ( slug === "contact" && document.getElementById("contactStructuredFields").style.display !== "none" ) { // Collect structured data const pagedata = { header: { title: document.getElementById("contactHeaderTitle").value, subtitle: document.getElementById("contactHeaderSubtitle").value, }, contactInfo: { phone: document.getElementById("contactPhone").value, email: document.getElementById("contactEmail").value, address: document.getElementById("contactAddress").value, }, businessHours: [], }; // Collect business hours const hourRows = document.getElementById("businessHoursList").children; for (let row of hourRows) { const days = row.querySelector('[data-field="days"]').value; const hours = row.querySelector('[data-field="hours"]').value; if (days && hours) { pagedata.businessHours.push({ days, hours }); } } // Generate HTML from structured data const generatedHTML = generateContactHTML(pagedata); formData.pagedata = pagedata; formData.content = generatedHTML; // Store HTML in content field formData.contenthtml = generatedHTML; // Also in contenthtml } // Check if this is privacy/shipping/returns page with structured fields else if ( (slug.includes("privacy") || slug.includes("shipping") || slug.includes("return")) && document.getElementById("privacyContentSection").style.display !== "none" ) { // Collect structured privacy data const pagedata = { header: { title: document.getElementById("privacyHeaderTitle").value, }, lastUpdated: document.getElementById("privacyLastUpdated").value, sections: [], }; // Collect sections const sectionDivs = document.getElementById( "privacySectionsContainer", ).children; for (let i = 0; i < sectionDivs.length; i++) { const sectionDiv = sectionDivs[i]; const title = sectionDiv.querySelector('[data-field="title"]').value; const editor = privacySectionEditors[i]; if (editor && title) { // Get content as Delta JSON const contentDelta = editor.getContents(); const contentHTML = editor.root.innerHTML; pagedata.sections.push({ title: title, content: JSON.stringify(contentDelta), // Store as Delta contentHTML: contentHTML, // Also store rendered HTML }); } } // Store the structured data formData.pagedata = pagedata; // Also generate and store as content for backwards compatibility const generatedHTML = generatePrivacyHTML(pagedata); formData.content = generatedHTML; formData.contenthtml = generatedHTML; } else { // Use Quill editor content for other pages const contentDelta = quillEditor.getContents(); const contentHTML = quillEditor.root.innerHTML; formData.content = JSON.stringify(contentDelta); // Store as Delta for editing formData.contenthtml = contentHTML; // Store rendered HTML for display if (contentDelta.ops.length === 0) { showError("Please fill in all required fields"); return; } } if (!formData.title || !formData.slug) { showError("Please fill in all required fields"); return; } try { const url = id ? `/api/admin/pages/${id}` : "/api/admin/pages"; const method = id ? "PUT" : "POST"; const response = await fetch(url, { method: method, headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify(formData), }); const data = await response.json(); if (data.success) { showSuccess( id ? "Page updated successfully" : "Page created successfully", ); pageModal.hide(); loadPages(); } else { showError(data.message || "Failed to save page"); } } catch (error) { console.error("Failed to save page:", error); showError("Failed to save page"); } } function generateContactHTML(pagedata) { const { header, contactInfo, businessHours } = pagedata; // Generate business hours HTML const businessHoursHTML = businessHours .map( (hour) => `

${escapeHtml( hour.days, )}

${escapeHtml(hour.hours)}

`, ) .join(""); return `

${escapeHtml(header.title)}

${escapeHtml(header.subtitle)}

Phone

${escapeHtml( contactInfo.phone, )}

Email

${escapeHtml( contactInfo.email, )}

Location

${escapeHtml( contactInfo.address, )}

Business Hours

${businessHoursHTML}
`; } function generatePrivacyHTML(pagedata) { const { header, lastUpdated, sections } = pagedata; // Generate sections HTML const sectionsHTML = sections .map((section) => { // Parse the content Delta if it's stored as JSON let contentHTML = section.contentHTML; if (!contentHTML && section.content) { try { // If we don't have contentHTML, try to get it from the content field contentHTML = section.content; } catch { contentHTML = section.content; } } return `

${escapeHtml(section.title)}

${contentHTML}
`; }) .join(""); return `
${lastUpdated ? `

Last updated: ${escapeHtml(lastUpdated)}

` : ""} ${sectionsHTML}
`; } async function deletePage(id, title) { // Show custom confirmation modal instead of browser confirm showConfirmation( `Are you sure you want to delete "${escapeHtml( title, )}"?

` + `This action cannot be undone.`, async () => { try { const response = await fetch(`/api/admin/pages/${id}`, { method: "DELETE", credentials: "include", }); const data = await response.json(); if (data.success) { showSuccess("Page deleted successfully"); loadPages(); } else { showError(data.message || "Failed to delete page"); } } catch (error) { console.error("Failed to delete page:", error); showError("Failed to delete page"); } }, ); } function slugify(text) { return text .toLowerCase() .replace(/[^\w\s-]/g, "") .replace(/[\s_-]+/g, "-") .replace(/^-+|-+$/g, ""); } function escapeHtml(text) { const map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", }; return text.replace(/[&<>"']/g, (m) => map[m]); } function formatDate(dateString) { return new Date(dateString).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", }); } function showSuccess(message) { showNotification(message, "success"); } function showError(message) { showNotification(message, "error"); } function showNotification(message, type) { const modal = new bootstrap.Modal( document.getElementById("notificationModal"), ); const modalContent = document.getElementById("notificationModalContent"); const modalHeader = document.getElementById("notificationModalHeader"); const modalIcon = document.getElementById("notificationModalIcon"); const modalTitleText = document.getElementById("notificationModalTitleText"); const modalBody = document.getElementById("notificationModalBody"); // Configure based on type if (type === "success") { modalContent.classList.remove("border-danger"); modalContent.classList.add("border-success"); modalContent.style.borderWidth = "3px"; modalHeader.classList.remove("bg-danger", "text-white"); modalHeader.classList.add("bg-success", "text-white"); modalIcon.className = "bi bi-check-circle-fill me-2"; modalTitleText.textContent = "Success"; modalBody.innerHTML = `

${escapeHtml( message, )}

`; } else { modalContent.classList.remove("border-success"); modalContent.classList.add("border-danger"); modalContent.style.borderWidth = "3px"; modalHeader.classList.remove("bg-success", "text-white"); modalHeader.classList.add("bg-danger", "text-white"); modalIcon.className = "bi bi-exclamation-triangle-fill me-2"; modalTitleText.textContent = "Error"; modalBody.innerHTML = `

${escapeHtml( message, )}

`; } modal.show(); } function showConfirmation(message, onConfirm) { const modal = new bootstrap.Modal(document.getElementById("confirmModal")); const modalBody = document.getElementById("confirmModalBody"); const confirmButton = document.getElementById("confirmModalButton"); // Set message modalBody.innerHTML = `

${message}

`; // Remove old event listeners by cloning button const newConfirmButton = confirmButton.cloneNode(true); confirmButton.parentNode.replaceChild(newConfirmButton, confirmButton); // Add new click handler newConfirmButton.addEventListener("click", () => { modal.hide(); onConfirm(); }); modal.show(); } // Modal Drag and Resize Functionality - DISABLED // function initializeModalDragResize() { // const modal = document.getElementById("pageModal"); // const modalDialog = modal.querySelector(".modal-dialog"); // const modalContent = modal.querySelector(".modal-content"); // const modalHeader = modal.querySelector(".modal-header"); // const resizeHandle = modal.querySelector(".modal-resize-handle"); // // let isDragging = false; // let isResizing = false; // let startX, startY, startWidth, startHeight, startLeft, startTop; // // // Dragging functionality // modalHeader.addEventListener("mousedown", function (e) { // // Don't drag if clicking on buttons // if ( // e.target.classList.contains("btn-close") || // e.target.classList.contains("btn-fullscreen") || // e.target.closest(".btn-close") || // e.target.closest(".btn-fullscreen") // ) { // return; // } // // isDragging = true; // const rect = modalDialog.getBoundingClientRect(); // startX = e.clientX - rect.left; // startY = e.clientY - rect.top; // modalDialog.style.margin = "0"; // modalDialog.style.position = "fixed"; // e.preventDefault(); // }); // // document.addEventListener("mousemove", function (e) { // if (isDragging) { // const newLeft = e.clientX - startX; // const newTop = e.clientY - startY; // // // Keep within viewport bounds // const maxLeft = window.innerWidth - modalDialog.offsetWidth; // const maxTop = window.innerHeight - modalDialog.offsetHeight; // // modalDialog.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px"; // modalDialog.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px"; // } // // if (isResizing) { // const newWidth = startWidth + (e.clientX - startX); // const newHeight = startHeight + (e.clientY - startY); // // // Set minimum and maximum sizes // const minWidth = 400; // const maxWidth = window.innerWidth - 40; // const minHeight = 300; // const maxHeight = window.innerHeight - 40; // // modalDialog.style.maxWidth = // Math.max(minWidth, Math.min(newWidth, maxWidth)) + "px"; // modalContent.style.maxHeight = // Math.max(minHeight, Math.min(newHeight, maxHeight)) + "px"; // modalContent.style.height = modalContent.style.maxHeight; // // // Adjust body height // const bodyMaxHeight = parseInt(modalContent.style.maxHeight) - 140 + "px"; // modal.querySelector(".modal-body").style.maxHeight = bodyMaxHeight; // } // }); // // document.addEventListener("mouseup", function () { // isDragging = false; // isResizing = false; // }); // // // Resizing functionality // if (resizeHandle) { // resizeHandle.addEventListener("mousedown", function (e) { // isResizing = true; // const rect = modalDialog.getBoundingClientRect(); // startX = e.clientX; // startY = e.clientY; // startWidth = rect.width; // startHeight = modalContent.offsetHeight; // e.preventDefault(); // e.stopPropagation(); // }); // } // // // Reset position when modal is closed // modal.addEventListener("hidden.bs.modal", function () { // modalDialog.style.position = ""; // modalDialog.style.left = ""; // modalDialog.style.top = ""; // modalDialog.style.margin = "1.75rem auto"; // }); // } // Toggle Fullscreen function toggleFullscreen() { const modal = document.getElementById("pageModal"); const icon = document.getElementById("fullscreenIcon"); if (modal.classList.contains("modal-fullscreen")) { modal.classList.remove("modal-fullscreen"); icon.classList.remove("bi-fullscreen-exit"); icon.classList.add("bi-arrows-fullscreen"); } else { modal.classList.add("modal-fullscreen"); icon.classList.remove("bi-arrows-fullscreen"); icon.classList.add("bi-fullscreen-exit"); } } // Toggle Content Expand/Collapse function toggleContentExpand(editorType) { if (editorType === "quillEditor") { const container = document.querySelector(".ql-container"); const icon = document.getElementById("quillExpandIcon"); if (container.classList.contains("expanded")) { container.classList.remove("expanded"); icon.classList.remove("bi-arrows-collapse"); icon.classList.add("bi-arrows-expand"); } else { container.classList.add("expanded"); icon.classList.remove("bi-arrows-expand"); icon.classList.add("bi-arrows-collapse"); } } else if (editorType === "contactStructuredFields") { const container = document.getElementById("contactStructuredFields"); const icon = document.getElementById("contactExpandIcon"); if (container.classList.contains("expanded")) { container.classList.remove("expanded"); icon.classList.remove("bi-arrows-collapse"); icon.classList.add("bi-arrows-expand"); } else { container.classList.add("expanded"); icon.classList.remove("bi-arrows-expand"); icon.classList.add("bi-arrows-collapse"); } } } // Simple Drag-to-Resize for Editor Content (function () { let resizeState = null; document.addEventListener("mousedown", function (e) { if (e.target.classList.contains("editor-resize-handle")) { e.preventDefault(); e.stopPropagation(); const targetId = e.target.getAttribute("data-target"); const targetElement = document.getElementById(targetId); console.log( "Resize handle clicked! Target:", targetId, "Element found:", !!targetElement, ); if (!targetElement) { console.error("Target element not found:", targetId); return; } resizeState = { target: targetElement, handle: e.target, startY: e.clientY, startHeight: targetElement.offsetHeight, }; console.log("Starting resize from height:", resizeState.startHeight); document.body.style.cursor = "nwse-resize"; document.body.style.userSelect = "none"; e.target.style.pointerEvents = "none"; } }); document.addEventListener("mousemove", function (e) { if (resizeState) { e.preventDefault(); e.stopPropagation(); const deltaY = e.clientY - resizeState.startY; const newHeight = Math.max( 200, Math.min(1200, resizeState.startHeight + deltaY), ); // Update target element height resizeState.target.style.height = newHeight + "px"; // For Quill editor (pageContentEditor), update the internal structure if (resizeState.target.id === "pageContentEditor") { // The target div BECOMES .ql-container when Quill initializes const editor = resizeState.target.querySelector(".ql-editor"); // Toolbar is a SIBLING, not a child - look in parent const parent = resizeState.target.parentElement; const toolbar = parent ? parent.querySelector(".ql-toolbar") : null; console.log( "pageContentEditor resize - editor:", !!editor, "toolbar:", !!toolbar, ); if (editor && toolbar) { const toolbarHeight = toolbar.offsetHeight; const editorHeight = newHeight - toolbarHeight; console.log( "Setting heights - toolbar:", toolbarHeight, "editor:", editorHeight, "total:", newHeight, ); resizeState.target.style.height = editorHeight + "px"; editor.style.height = editorHeight + "px"; } else if (editor) { // Fallback: just resize the editor if no toolbar found console.log("Toolbar not found, resizing editor directly"); editor.style.height = newHeight + "px"; } } // For About editor (aboutContentEditor) if (resizeState.target.id === "aboutContentEditor") { // The target div BECOMES .ql-container when Quill initializes const editor = resizeState.target.querySelector(".ql-editor"); // Toolbar is a SIBLING, not a child - look in parent const parent = resizeState.target.parentElement; const toolbar = parent ? parent.querySelector(".ql-toolbar") : null; console.log( "aboutContentEditor resize - editor:", !!editor, "toolbar:", !!toolbar, ); if (editor && toolbar) { const toolbarHeight = toolbar.offsetHeight; const editorHeight = newHeight - toolbarHeight; console.log( "Setting heights - toolbar:", toolbarHeight, "editor:", editorHeight, "total:", newHeight, ); resizeState.target.style.height = editorHeight + "px"; editor.style.height = editorHeight + "px"; } else if (editor) { // Fallback: just resize the editor if no toolbar found console.log("Toolbar not found, resizing editor directly"); editor.style.height = newHeight + "px"; } } } }); document.addEventListener("mouseup", function () { if (resizeState) { resizeState.handle.style.pointerEvents = ""; resizeState = null; document.body.style.cursor = ""; document.body.style.userSelect = ""; } }); })();