// Blog Management JavaScript let postsData = []; let postModal; let quillEditor; let isModalExpanded = false; document.addEventListener("DOMContentLoaded", function () { postModal = new bootstrap.Modal(document.getElementById("postModal")); initializeQuillEditor(); checkAuth().then((authenticated) => { if (authenticated) { loadPosts(); } }); const urlParams = new URLSearchParams(window.location.search); if (urlParams.get("action") === "create") { showCreatePost(); } // Auto-generate slug from title document.getElementById("postTitle")?.addEventListener("input", function () { if (!document.getElementById("postId").value) { document.getElementById("postSlug").value = slugify(this.value); } }); }); function resetModalSize() { const modalDialog = document.querySelector("#postModal .modal-dialog"); const expandIcon = document.getElementById("expandIcon"); const expandText = document.querySelector("#btnExpandModal span"); const editor = document.getElementById("postContentEditor"); if (modalDialog && expandIcon && expandText && editor) { modalDialog.classList.remove("modal-fullscreen"); modalDialog.classList.add("modal-xl"); expandIcon.className = "bi bi-arrows-fullscreen"; expandText.textContent = "Expand"; editor.style.height = "400px"; const container = editor.querySelector(".ql-container"); if (container) { container.style.height = "calc(400px - 42px)"; } isModalExpanded = false; } } function toggleModalSize() { const modalDialog = document.querySelector("#postModal .modal-dialog"); const expandIcon = document.getElementById("expandIcon"); const expandText = document.querySelector("#btnExpandModal span"); const editor = document.getElementById("postContentEditor"); if (!modalDialog || !expandIcon || !expandText || !editor) { console.error("Modal elements not found"); return; } if (isModalExpanded) { // Collapse to normal size modalDialog.classList.remove("modal-fullscreen"); modalDialog.classList.add("modal-xl"); expandIcon.className = "bi bi-arrows-fullscreen"; expandText.textContent = "Expand"; editor.style.height = "400px"; const container = editor.querySelector(".ql-container"); if (container) { container.style.height = "calc(400px - 42px)"; } isModalExpanded = false; } else { // Expand to fullscreen modalDialog.classList.remove("modal-xl"); modalDialog.classList.add("modal-fullscreen"); expandIcon.className = "bi bi-fullscreen-exit"; expandText.textContent = "Collapse"; editor.style.height = "60vh"; const container = editor.querySelector(".ql-container"); if (container) { container.style.height = "calc(60vh - 42px)"; } isModalExpanded = true; } } function initializeQuillEditor() { quillEditor = new Quill("#postContentEditor", { theme: "snow", placeholder: "Write your blog post content here...", modules: { toolbar: [ [{ header: [1, 2, 3, false] }], ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }], [{ color: [] }, { background: [] }], ["link", "image"], ["blockquote", "code-block"], ["clean"], ], }, }); } function openMediaLibraryForFeaturedImage() { // 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 = ''; 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; `; 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(); } }; // Setup media selection handler window.handleMediaSelection = function (media) { const mediaItem = Array.isArray(media) ? media[0] : media; if (mediaItem && mediaItem.url) { document.getElementById("postFeaturedImage").value = mediaItem.url; updateFeaturedImagePreview(mediaItem.url); showToast("Featured image selected", "success"); } closeMediaLibrary(); }; } function closeMediaLibrary() { const backdrop = document.getElementById("mediaLibraryBackdrop"); if (backdrop) { backdrop.remove(); } } function updateFeaturedImagePreview(url) { const preview = document.getElementById("featuredImagePreview"); if (url) { preview.innerHTML = `
`; } else { preview.innerHTML = ""; } } function removeFeaturedImage() { document.getElementById("postFeaturedImage").value = ""; updateFeaturedImagePreview(""); showToast("Featured image removed", "info"); } async function loadPosts() { try { const response = await fetch("/api/admin/blog", { credentials: "include" }); const data = await response.json(); console.log("Blog API Response:", data); if (data.success) { postsData = data.posts; console.log("Loaded posts:", postsData); renderPosts(postsData); } else { console.error("API returned success=false:", data); const tbody = document.getElementById("postsTableBody"); tbody.innerHTML = `

Failed to load posts: ${ data.message || "Unknown error" }

`; } } catch (error) { console.error("Failed to load posts:", error); const tbody = document.getElementById("postsTableBody"); tbody.innerHTML = `

Error loading posts. Please refresh the page.

`; } } function renderPosts(posts) { const tbody = document.getElementById("postsTableBody"); if (posts.length === 0) { tbody.innerHTML = `

No blog posts found

`; return; } tbody.innerHTML = posts .map( (p) => ` ${escapeHtml(String(p.id))} ${escapeHtml(p.title)} ${escapeHtml(p.slug)} ${escapeHtml((p.excerpt || "").substring(0, 40))}... ${p.ispublished ? "Published" : "Draft"} ${formatDate(p.createdat)} ` ) .join(""); } function filterPosts() { const searchTerm = document.getElementById("searchInput").value.toLowerCase(); const filtered = postsData.filter( (p) => p.title.toLowerCase().includes(searchTerm) || p.slug.toLowerCase().includes(searchTerm) ); renderPosts(filtered); } function showCreatePost() { document.getElementById("modalTitle").textContent = "Create Blog Post"; document.getElementById("postForm").reset(); document.getElementById("postId").value = ""; document.getElementById("postPublished").checked = false; document.getElementById("postFeaturedImage").value = ""; updateFeaturedImagePreview(""); if (quillEditor) { quillEditor.setContents([]); } resetModalSize(); postModal.show(); } async function editPost(id) { try { const response = await fetch(`/api/admin/blog/${id}`, { credentials: "include", }); const data = await response.json(); if (data.success) { const post = data.post; document.getElementById("modalTitle").textContent = "Edit Blog Post"; document.getElementById("postId").value = post.id; document.getElementById("postTitle").value = post.title; document.getElementById("postSlug").value = post.slug; document.getElementById("postExcerpt").value = post.excerpt || ""; // Set Quill content if (quillEditor) { quillEditor.root.innerHTML = post.content || ""; } // Set featured image const featuredImage = post.featuredimage || post.imageurl || ""; document.getElementById("postFeaturedImage").value = featuredImage; updateFeaturedImagePreview(featuredImage); document.getElementById("postMetaTitle").value = post.metatitle || ""; document.getElementById("postMetaDescription").value = post.metadescription || ""; document.getElementById("postPublished").checked = post.ispublished; resetModalSize(); postModal.show(); } } catch (error) { console.error("Failed to load post:", error); showToast("Failed to load post details", "error"); } } async function savePost() { const id = document.getElementById("postId").value; // Get content from Quill editor const content = quillEditor ? quillEditor.root.innerHTML : ""; const formData = { title: document.getElementById("postTitle").value, slug: document.getElementById("postSlug").value, excerpt: document.getElementById("postExcerpt").value, content: content, featuredimage: document.getElementById("postFeaturedImage").value, metatitle: document.getElementById("postMetaTitle").value, metadescription: document.getElementById("postMetaDescription").value, ispublished: document.getElementById("postPublished").checked, }; if (!formData.title || !formData.slug || !formData.content) { showToast("Please fill in all required fields", "error"); return; } try { const url = id ? `/api/admin/blog/${id}` : "/api/admin/blog"; 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) { showToast( id ? "Post updated successfully" : "Post created successfully", "success" ); postModal.hide(); loadPosts(); } else { showToast(data.message || "Failed to save post", "error"); } } catch (error) { console.error("Failed to save post:", error); showToast("Failed to save post", "error"); } } async function deletePost(id, title) { if (!confirm(`Are you sure you want to delete "${title}"?`)) return; try { const response = await fetch(`/api/admin/blog/${id}`, { method: "DELETE", credentials: "include", }); const data = await response.json(); if (data.success) { showToast("Post deleted successfully", "success"); loadPosts(); } else { showToast(data.message || "Failed to delete post", "error"); } } catch (error) { console.error("Failed to delete post:", error); showToast("Failed to delete post", "error"); } } function showToast(message, type = "info") { const toastContainer = document.getElementById("toastContainer") || createToastContainer(); const toast = document.createElement("div"); toast.className = `toast toast-${type}`; const icons = { success: "check-circle-fill", error: "exclamation-triangle-fill", warning: "exclamation-circle-fill", info: "info-circle-fill", }; toast.innerHTML = ` ${message} `; toastContainer.appendChild(toast); setTimeout(() => toast.classList.add("show"), 10); setTimeout(() => { toast.classList.remove("show"); setTimeout(() => toast.remove(), 300); }, 3000); } function createToastContainer() { const container = document.createElement("div"); container.id = "toastContainer"; container.style.cssText = "position: fixed; top: 80px; right: 20px; z-index: 9999;"; document.body.appendChild(container); return container; } 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", }); }