// 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",
});
}