/** * Admin JavaScript - Consolidated functionality for all admin pages * Provides shared utilities, API interactions, and page-specific features */ // ==================== UTILITY FUNCTIONS ==================== /** * Displays a message to the user with auto-hide functionality * @param {string} text - The message text to display * @param {string} type - Message type ('success', 'error', 'info', etc.) */ function showMessage(text, type) { let messageDiv = document.getElementById("message"); if (!messageDiv) { messageDiv = document.createElement("div"); messageDiv.id = "message"; document.body.insertBefore(messageDiv, document.body.firstChild); } messageDiv.className = `message ${type}`; messageDiv.textContent = text; messageDiv.style.display = "block"; setTimeout(() => (messageDiv.style.display = "none"), 5000); } /** * Escapes HTML characters to prevent XSS * @param {string} text - Text to escape * @returns {string} Escaped HTML string */ const escapeHtml = (text) => { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; }; /** * Copies text to clipboard with fallback for older browsers * @param {string} text - Text to copy * @returns {Promise} Success status */ async function copyToClipboard(text) { if (navigator.clipboard?.writeText) { try { await navigator.clipboard.writeText(text); return true; } catch (e) { // Fall through to fallback } } // Fallback method const tmp = document.createElement("textarea"); tmp.value = text; tmp.setAttribute("readonly", ""); tmp.style.position = "absolute"; tmp.style.left = "-9999px"; document.body.appendChild(tmp); tmp.select(); try { const ok = document.execCommand("copy"); document.body.removeChild(tmp); return ok; } catch (err) { document.body.removeChild(tmp); return false; } } // ==================== SETTINGS MANAGEMENT ==================== /** * Loads settings from API and displays them */ function loadSettingsForList() { fetch("/admin/api/settings") .then((response) => response.json()) .then((data) => { if (data.status === "ok") { appSettings = data.settings || {}; displaySettings(); } else { showMessage( "Error loading settings: " + (data.message || "Unknown error"), "error" ); } }) .catch((error) => { console.error("Error:", error); showMessage("Error loading settings", "error"); }); } /** * Fetches embed settings from API * @returns {Promise} Settings object or empty object on error */ async function fetchEmbedSettings() { try { const response = await fetch("/admin/api/settings"); const data = await response.json(); return data.status === "ok" && data.settings ? data.settings : {}; } catch (err) { console.error("Failed to load embed settings", err); return {}; } } /** * Saves a setting via API * @param {string} key - Setting key * @param {string} inputId - Input element ID */ async function saveEmbedSetting(key, inputId) { const input = document.getElementById(inputId); if (!input) return showMessage("Input not found", "error"); const value = (input.value || "").toString().trim(); if (!value) return showMessage("Value cannot be empty", "error"); try { const response = await fetch( `/admin/api/settings/${encodeURIComponent(key)}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ value }), } ); const data = await response.json(); if (data.status === "ok") { showMessage("Setting saved", "success"); // Rebuild textarea values to reflect the new size if (typeof loadEmbedSettingsAndInit === "function") { loadEmbedSettingsAndInit(); } } else { showMessage("Failed to save setting: " + (data.message || ""), "error"); } } catch (err) { console.error("Failed to save setting", err); showMessage("Failed to save setting", "error"); } } // ==================== EMAIL SETTINGS ==================== const emailSettingsFieldConfig = { smtp_host: { selector: "#smtpHost", type: "text" }, smtp_port: { selector: "#smtpPort", type: "number" }, smtp_username: { selector: "#smtpUsername", type: "text" }, smtp_password: { selector: "#smtpPassword", type: "text" }, smtp_sender: { selector: "#smtpSender", type: "text" }, smtp_recipients: { selector: "#smtpRecipients", type: "textarea" }, smtp_use_tls: { selector: "#smtpUseTls", type: "checkbox" }, notify_contact_form: { selector: "#notifyContactForm", type: "checkbox" }, notify_newsletter_signups: { selector: "#notifyNewsletter", type: "checkbox", }, }; function normalizeRecipientsInput(value) { if (Array.isArray(value)) return value.join(", "); if (!value) return ""; return value; } function applyEmailSettingsForm(settings) { Object.entries(emailSettingsFieldConfig).forEach(([field, config]) => { const element = document.querySelector(config.selector); if (!element) return; const fieldValue = settings[field]; switch (config.type) { case "checkbox": { const normalized = fieldValue === true || fieldValue === "true" || fieldValue === 1 || fieldValue === "1"; element.checked = normalized; break; } case "number": { if (typeof fieldValue === "number" && Number.isFinite(fieldValue)) { element.value = fieldValue; } else { const parsed = parseInt(fieldValue, 10); element.value = Number.isFinite(parsed) ? parsed : ""; } break; } case "textarea": element.value = normalizeRecipientsInput(fieldValue); break; default: element.value = fieldValue || ""; break; } }); } function collectEmailSettingsForm() { const payload = {}; Object.entries(emailSettingsFieldConfig).forEach(([field, config]) => { const element = document.querySelector(config.selector); if (!element) return; switch (config.type) { case "checkbox": payload[field] = element.checked; break; case "number": if (element.value === "") { payload[field] = ""; } else { const parsed = parseInt(element.value, 10); payload[field] = Number.isFinite(parsed) ? parsed : element.value; } break; case "textarea": payload[field] = element.value; break; default: payload[field] = element.value; break; } }); return payload; } function clearEmailFieldErrors() { Object.values(emailSettingsFieldConfig).forEach((config) => { const element = document.querySelector(config.selector); if (element) element.classList.remove("input-error"); }); } function applyEmailFieldErrors(errors) { Object.entries(errors || {}).forEach(([field]) => { const config = emailSettingsFieldConfig[field]; if (!config) return; const element = document.querySelector(config.selector); if (element) element.classList.add("input-error"); }); } async function loadEmailSettings() { try { const response = await fetch("/admin/api/email-settings"); const data = await response.json(); if (data.status === "ok" && data.settings) { applyEmailSettingsForm(data.settings); } else { showMessage(data.message || "Failed to load email settings", "error"); } } catch (error) { console.error("Failed to load email settings", error); showMessage("Failed to load email settings", "error"); } } async function submitEmailSettings(event) { event.preventDefault(); clearEmailFieldErrors(); const payload = collectEmailSettingsForm(); try { const response = await fetch("/admin/api/email-settings", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); const data = await response.json(); if (response.ok && data.status === "ok") { showMessage("Email settings updated successfully.", "success"); applyEmailSettingsForm(data.settings || {}); } else { applyEmailFieldErrors(data.errors); const errorSummary = Array.isArray(data.errors) ? data.errors.join(" ") : data && data.errors ? Object.values(data.errors).join(" ") : ""; const message = [ data.message || "Failed to update email settings", errorSummary, ] .filter(Boolean) .join(" "); showMessage(message, "error"); } } catch (error) { console.error("Failed to update email settings", error); showMessage("Failed to update email settings", "error"); } } // ==================== EMBED MANAGEMENT ==================== /** * Initializes embed settings and populates form fields */ async function loadEmbedSettingsAndInit() { const origin = window.location && window.location.origin ? window.location.origin : "http://your-server-domain"; // Default dimensions let contactWidth = "600", contactHeight = "400"; let newsletterWidth = "600", newsletterHeight = "300"; try { const settings = await fetchEmbedSettings(); contactWidth = settings.embed_contact_width || contactWidth; contactHeight = settings.embed_contact_height || contactHeight; newsletterWidth = settings.embed_newsletter_width || newsletterWidth; newsletterHeight = settings.embed_newsletter_height || newsletterHeight; } catch (err) { console.error("Error loading embed settings", err); } // Update input fields const inputs = { contactWidth, contactHeight, newsletterWidth, newsletterHeight, }; Object.entries(inputs).forEach(([id, value]) => { const element = document.getElementById(id); if (element) element.value = value; }); // Update iframe code textareas const contactTextarea = document.getElementById("iframeCode"); if (contactTextarea) { contactTextarea.value = ``; } const newsletterTextarea = document.getElementById("iframeNewsletterCode"); if (newsletterTextarea) { newsletterTextarea.value = ``; } const contactPreview = document.getElementById("contactFormPreview"); if (contactPreview) { contactPreview.innerHTML = ``; } const newsletterPreview = document.getElementById("newsletterFormPreview"); if (newsletterPreview) { newsletterPreview.innerHTML = ``; } } /** * Copies contact iframe code to clipboard */ function copyIframeCode() { const textarea = document.getElementById("iframeCode"); if (!textarea) return showMessage("Contact iframe not found", "error"); copyToClipboard(textarea.value).then((ok) => showMessage( ok ? "Contact iframe code copied to clipboard!" : "Failed to copy contact iframe code", ok ? "success" : "error" ) ); } /** * Copies newsletter iframe code to clipboard */ function copyNewsletterIframeCode() { const textarea = document.getElementById("iframeNewsletterCode"); if (!textarea) return showMessage("Newsletter iframe not found", "error"); copyToClipboard(textarea.value).then((ok) => showMessage( ok ? "Newsletter iframe code copied to clipboard!" : "Failed to copy newsletter iframe code", ok ? "success" : "error" ) ); } // ==================== DASHBOARD ==================== /** * Loads and displays dashboard statistics */ async function loadDashboardStats() { try { const [contactRes, newsletterRes, settingsRes] = await Promise.all([ fetch("/admin/api/contact?page=1&per_page=1"), fetch("/admin/api/newsletter?page=1&per_page=1"), fetch("/admin/api/settings"), ]); if (contactRes.ok) { const data = await contactRes.json(); document.getElementById("contact-count").textContent = data.pagination.total; } if (newsletterRes.ok) { const data = await newsletterRes.json(); document.getElementById("newsletter-count").textContent = data.pagination.total; } if (settingsRes.ok) { const data = await settingsRes.json(); document.getElementById("settings-count").textContent = Object.keys( data.settings || {} ).length; } } catch (error) { console.error("Failed to load dashboard stats:", error); } } // ==================== EMAIL TEMPLATES ==================== let emailTemplatesCache = []; let activeEmailTemplateId = null; function setEmailTemplateMessage(text, type = "info") { const messageEl = document.getElementById("templateMessage"); if (!messageEl) return; if (!text) { messageEl.textContent = ""; messageEl.className = "message"; messageEl.style.display = "none"; return; } messageEl.textContent = text; messageEl.className = `message ${type}`; messageEl.style.display = "block"; } function highlightSelectedTemplate(templateId) { const buttons = document.querySelectorAll( "#emailTemplatesList button[data-template-id]" ); buttons.forEach((button) => { if (button.dataset.templateId === templateId) { button.classList.add("active"); } else { button.classList.remove("active"); } }); } function renderEmailTemplatesList(templates) { const listEl = document.getElementById("emailTemplatesList"); if (!listEl) return; if (!templates.length) { listEl.innerHTML = '

No editable templates are configured.

'; return; } listEl.innerHTML = templates .map( (template) => ` ` ) .join(""); listEl.querySelectorAll("button[data-template-id]").forEach((button) => { button.addEventListener("click", () => { selectEmailTemplate(button.dataset.templateId); }); }); } function disableEmailTemplateEditor(disabled) { const form = document.getElementById("emailTemplateForm"); const textarea = document.getElementById("templateContent"); const saveButton = document.getElementById("saveTemplateButton"); if (form) form.classList.toggle("disabled", disabled); if (textarea) textarea.disabled = disabled; if (saveButton) saveButton.disabled = disabled; } async function loadEmailTemplatesPage() { setEmailTemplateMessage("Loading email templates...", "info"); disableEmailTemplateEditor(true); try { const response = await fetch("/admin/api/email-templates"); const data = await response.json(); if (data.status !== "ok") { throw new Error(data.message || "Failed to load email templates"); } emailTemplatesCache = Array.isArray(data.templates) ? data.templates : []; renderEmailTemplatesList(emailTemplatesCache); setEmailTemplateMessage(""); if (emailTemplatesCache.length) { selectEmailTemplate(emailTemplatesCache[0].id); } } catch (error) { console.error("Failed to load email templates", error); setEmailTemplateMessage("Failed to load email templates", "error"); } } async function selectEmailTemplate(templateId) { if (!templateId) return; setEmailTemplateMessage("Loading template...", "info"); disableEmailTemplateEditor(true); try { const response = await fetch(`/admin/api/email-templates/${templateId}`); const data = await response.json(); if (data.status !== "ok" || !data.template) { throw new Error(data.message || "Failed to load template"); } const { name, description, content, id } = data.template; const titleEl = document.getElementById("templateTitle"); const descriptionEl = document.getElementById("templateDescription"); const textarea = document.getElementById("templateContent"); if (titleEl) titleEl.textContent = name; if (descriptionEl) descriptionEl.textContent = description; if (textarea) textarea.value = content || ""; activeEmailTemplateId = id; highlightSelectedTemplate(id); setEmailTemplateMessage(""); disableEmailTemplateEditor(false); } catch (error) { console.error("Failed to load template", error); setEmailTemplateMessage("Failed to load template", "error"); } } async function saveEmailTemplate(event) { if (event) event.preventDefault(); if (!activeEmailTemplateId) return; const textarea = document.getElementById("templateContent"); if (!textarea) return; const content = textarea.value || ""; setEmailTemplateMessage("Saving template...", "info"); disableEmailTemplateEditor(true); try { const response = await fetch( `/admin/api/email-templates/${activeEmailTemplateId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content }), } ); const data = await response.json(); if (response.ok && data.status === "ok") { setEmailTemplateMessage("Template saved successfully.", "success"); disableEmailTemplateEditor(false); } else { throw new Error(data.message || "Failed to save template"); } } catch (error) { console.error("Failed to save template", error); setEmailTemplateMessage("Failed to save template", "error"); disableEmailTemplateEditor(false); } } // ==================== NEWSLETTER CREATION ==================== let newsletterStats = {}; /** * Loads newsletter statistics */ function loadNewsletterStats() { fetch("/admin/api/newsletter?page=1&per_page=1") .then((response) => response.json()) .then((data) => { if (data.status === "ok") { const total = data.pagination.total; document.getElementById("totalSubscribers").textContent = total; document.getElementById("activeSubscribers").textContent = total; // Assume all active newsletterStats.totalSubscribers = total; } }) .catch((error) => console.error("Error loading subscriber stats:", error)) .finally(() => { const lastSentEl = document.getElementById("lastSent"); if (lastSentEl) lastSentEl.textContent = "N/A"; }); } /** * Generates newsletter preview */ function previewNewsletter() { const subject = document.getElementById("subject").value.trim(); const content = document.getElementById("content").value.trim(); const senderName = document.getElementById("senderName").value.trim(); if (!subject || !content) { return showMessage( "Subject and content are required for preview.", "error" ); } const previewContent = document.getElementById("previewContent"); if (previewContent) { previewContent.innerHTML = `

${escapeHtml(subject)}

${ senderName ? `

From: ${escapeHtml(senderName)}

` : "" }
${content.replace(/\n/g, "
")}
`; } const previewSection = document.getElementById("previewSection"); if (previewSection) previewSection.classList.remove("hidden"); showMessage("Newsletter preview generated.", "info"); } /** * Saves newsletter as draft */ function saveDraft() { const form = document.getElementById("newsletterForm"); if (!form) return; const formData = new FormData(form); const newsletterData = { subject: formData.get("subject"), content: formData.get("content"), sender_name: formData.get("sender_name"), send_date: formData.get("send_date"), status: "draft", }; if (!newsletterData.subject || !newsletterData.content) { return showMessage("Subject and content are required.", "error"); } fetch("/admin/api/newsletters", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newsletterData), }) .then((response) => response.json()) .then((data) => { showMessage( data.status === "ok" ? "Newsletter draft saved successfully!" : data.message || "Failed to save draft.", data.status === "ok" ? "success" : "error" ); }) .catch((error) => { console.error("Error saving draft:", error); showMessage("Failed to save draft.", "error"); }); } /** * Sends newsletter to subscribers */ function sendNewsletter() { const form = document.getElementById("newsletterForm"); if (!form) return; const formData = new FormData(form); const newsletterData = { subject: formData.get("subject"), content: formData.get("content"), sender_name: formData.get("sender_name"), send_date: formData.get("send_date"), status: "sent", }; if (!newsletterData.subject || !newsletterData.content) { return showMessage("Subject and content are required.", "error"); } if ( !confirm( `Are you sure you want to send this newsletter to ${ newsletterStats.totalSubscribers || 0 } subscribers?` ) ) { return; } // Save and send fetch("/admin/api/newsletters", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newsletterData), }) .then((response) => response.json()) .then((data) => { if (data.status === "ok") { const newsletterId = data.newsletter_id; return fetch(`/admin/api/newsletters/${newsletterId}/send`, { method: "POST", }); } else { throw new Error(data.message || "Failed to save newsletter."); } }) .then((response) => response.json()) .then((data) => { showMessage( data.status === "ok" ? `Newsletter sent successfully to ${data.sent_count} subscribers!` : data.message || "Failed to send newsletter.", data.status === "ok" ? "success" : "error" ); }) .catch((error) => { console.error("Error sending newsletter:", error); showMessage("Failed to send newsletter.", "error"); }); } /** * Clears newsletter form with confirmation */ function clearForm() { if ( !confirm( "Are you sure you want to clear the form? All unsaved changes will be lost." ) ) { return; } const form = document.getElementById("newsletterForm"); if (form) form.reset(); const previewSection = document.getElementById("previewSection"); if (previewSection) previewSection.classList.add("hidden"); showMessage("Form cleared.", "info"); } // ==================== NEWSLETTER SUBSCRIBERS ==================== let currentPage = 1; let currentFilters = { email: "", sort_by: "subscribed_at", sort_order: "desc", }; /** * Applies filters to subscriber list */ function applyFilters() { const emailFilter = document.getElementById("emailFilter"); const sortBy = document.getElementById("sortBy"); const sortOrder = document.getElementById("sortOrder"); if (emailFilter) currentFilters.email = emailFilter.value.trim(); if (sortBy) currentFilters.sort_by = sortBy.value; if (sortOrder) currentFilters.sort_order = sortOrder.value; currentPage = 1; loadSubscribers(); } /** * Clears all filters */ function clearFilters() { const emailFilter = document.getElementById("emailFilter"); const sortBy = document.getElementById("sortBy"); const sortOrder = document.getElementById("sortOrder"); if (emailFilter) emailFilter.value = ""; if (sortBy) sortBy.value = "subscribed_at"; if (sortOrder) sortOrder.value = "desc"; currentFilters = { email: "", sort_by: "subscribed_at", sort_order: "desc" }; currentPage = 1; loadSubscribers(); } /** * Loads subscribers with current filters and pagination */ function loadSubscribers() { const loading = document.getElementById("loading"); const table = document.getElementById("subscribersTable"); const pagination = document.getElementById("pagination"); if (loading) loading.style.display = "block"; if (table) table.style.display = "none"; if (pagination) pagination.style.display = "none"; const params = new URLSearchParams({ page: currentPage, per_page: 50, sort_by: currentFilters.sort_by, sort_order: currentFilters.sort_order, }); if (currentFilters.email) params.append("email", currentFilters.email); fetch(`/admin/api/newsletter?${params}`) .then((response) => response.json()) .then((data) => { if (data.status === "ok") { displaySubscribers(data.subscribers); updatePagination(data.pagination); } else { showMessage( "Error loading subscribers: " + (data.message || "Unknown error"), "error" ); } }) .catch((error) => { console.error("Error:", error); showMessage("Error loading subscribers", "error"); }) .finally(() => { if (loading) loading.style.display = "none"; }); } /** * Displays subscribers in table * @param {Array} subscribers - Array of subscriber objects */ function displaySubscribers(subscribers) { const tbody = document.getElementById("subscribersBody"); if (!tbody) return; tbody.innerHTML = ""; if (subscribers.length === 0) { tbody.innerHTML = 'No subscribers found'; } else { subscribers.forEach((subscriber) => { const row = document.createElement("tr"); row.innerHTML = ` ${escapeHtml(subscriber.email)} ${new Date(subscriber.subscribed_at).toLocaleDateString()} `; tbody.appendChild(row); }); } const table = document.getElementById("subscribersTable"); if (table) table.style.display = "table"; } /** * Updates pagination controls * @param {Object} pagination - Pagination data */ function updatePagination(pagination) { const pageInfo = document.getElementById("pageInfo"); const prevBtn = document.getElementById("prevBtn"); const nextBtn = document.getElementById("nextBtn"); if (pageInfo) pageInfo.textContent = `Page ${pagination.page} of ${pagination.pages} (${pagination.total} total)`; if (prevBtn) prevBtn.disabled = pagination.page <= 1; if (nextBtn) nextBtn.disabled = pagination.page >= pagination.pages; const paginationDiv = document.getElementById("pagination"); if (paginationDiv) paginationDiv.style.display = "flex"; } /** * Changes to a different page * @param {number} page - Page number to navigate to */ function changePage(page) { currentPage = page; loadSubscribers(); } /** * Unsubscribes a user from newsletter * @param {string} email - Email address to unsubscribe */ function unsubscribe(email) { if (!confirm(`Are you sure you want to unsubscribe ${email}?`)) return; fetch("/api/newsletter", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email }), }) .then((response) => response.json()) .then((data) => { if (data.status === "ok") { showMessage("Subscriber unsubscribed successfully", "success"); loadSubscribers(); } else { showMessage( "Error unsubscribing: " + (data.message || "Unknown error"), "error" ); } }) .catch((error) => { console.error("Error:", error); showMessage("Error unsubscribing subscriber", "error"); }); } // ==================== CONTACT SUBMISSIONS ==================== let submissionsCurrentPage = 1; let submissionsSortBy = "created_at"; let submissionsSortOrder = "desc"; /** * Clears contact submission filters */ function clearSubmissionFilters() { const email = document.getElementById("email"); const dateFrom = document.getElementById("date_from"); const dateTo = document.getElementById("date_to"); const perPage = document.getElementById("per_page"); if (email) email.value = ""; if (dateFrom) dateFrom.value = ""; if (dateTo) dateTo.value = ""; if (perPage) perPage.value = "50"; submissionsCurrentPage = 1; submissionsSortBy = "created_at"; submissionsSortOrder = "desc"; loadSubmissions(); } /** * Loads contact submissions with filters */ function loadSubmissions() { const loading = document.getElementById("loading"); const table = document.getElementById("submissionsTable"); if (loading) loading.style.display = "block"; if (table) table.style.opacity = "0.5"; const perPage = document.getElementById("per_page"); const email = document.getElementById("email"); const dateFrom = document.getElementById("date_from"); const dateTo = document.getElementById("date_to"); const params = new URLSearchParams({ page: submissionsCurrentPage, per_page: perPage?.value || "50", sort_by: submissionsSortBy, sort_order: submissionsSortOrder, email: email?.value || "", date_from: dateFrom?.value || "", date_to: dateTo?.value || "", }); fetch(`/api/contact?${params}`) .then((response) => response.json()) .then((data) => { if (data.status === "ok") { displaySubmissions(data.submissions); displaySubmissionPagination(data.pagination); } else { showMessage( "Error loading submissions: " + (data.message || "Unknown error"), "error" ); } }) .catch((error) => { console.error("Error:", error); showMessage("Error loading submissions", "error"); }) .finally(() => { if (loading) loading.style.display = "none"; if (table) table.style.opacity = "1"; }); } /** * Displays contact submissions in table * @param {Array} submissions - Array of submission objects */ function displaySubmissions(submissions) { const tbody = document.getElementById("submissionsBody"); if (!tbody) return; if (submissions.length === 0) { tbody.innerHTML = 'No submissions found'; return; } tbody.innerHTML = submissions .map( (submission) => ` ${submission.id} ${escapeHtml(submission.name)} ${escapeHtml(submission.email)} ${escapeHtml(submission.company || "")} ${escapeHtml(submission.message)} ${new Date(submission.created_at).toLocaleString()} ` ) .join(""); } /** * Updates submission pagination controls * @param {Object} pagination - Pagination data */ function displaySubmissionPagination(pagination) { const paginationDiv = document.getElementById("pagination"); if (!paginationDiv) return; if (pagination.pages <= 1) { paginationDiv.innerHTML = ""; return; } let buttons = []; // Previous button buttons.push( `` ); // Page numbers const startPage = Math.max(1, pagination.page - 2); const endPage = Math.min(pagination.pages, pagination.page + 2); for (let i = startPage; i <= endPage; i++) { buttons.push( `` ); } // Next button buttons.push( `` ); paginationDiv.innerHTML = buttons.join(""); } /** * Changes to a different submission page * @param {number} page - Page number to navigate to */ function changeSubmissionPage(page) { submissionsCurrentPage = page; loadSubmissions(); window.scrollTo(0, 0); } /** * Deletes a contact submission * @param {number} id - Submission ID to delete */ function deleteSubmission(id) { if (!confirm("Are you sure you want to delete this submission?")) return; fetch(`/api/contact/${id}`, { method: "DELETE" }) .then((response) => response.json()) .then((data) => { if (data.status === "ok") { showMessage("Submission deleted successfully", "success"); loadSubmissions(); } else { showMessage( "Error deleting submission: " + (data.message || "Unknown error"), "error" ); } }) .catch((error) => { console.error("Error:", error); showMessage("Error deleting submission", "error"); }); } // ==================== INITIALIZATION ==================== // Global admin object for external access window.admin = { showMessage, escapeHtml, fetchEmbedSettings, saveEmbedSetting, copyIframeCode, copyNewsletterIframeCode, loadEmbedSettingsAndInit, loadDashboardStats, loadEmailTemplatesPage, selectEmailTemplate, saveEmailTemplate, loadEmailSettings, submitEmailSettings, loadNewsletterStats, previewNewsletter, saveDraft, sendNewsletter, clearForm, applyFilters, clearFilters, loadSubscribers, displaySubscribers, updatePagination, changePage, unsubscribe, clearSubmissionFilters, loadSubmissions, displaySubmissions, displaySubmissionPagination, changeSubmissionPage, deleteSubmission, }; // Auto-initialize based on page content document.addEventListener("DOMContentLoaded", function () { // Embed page if ( document.getElementById("iframeCode") || document.getElementById("iframeNewsletterCode") ) { loadEmbedSettingsAndInit(); } // Dashboard if (document.getElementById("contact-count")) { loadDashboardStats(); } // Email templates const emailTemplatesPage = document.getElementById("emailTemplatesPage"); if (emailTemplatesPage) { loadEmailTemplatesPage(); const form = document.getElementById("emailTemplateForm"); if (form) { form.addEventListener("submit", (event) => { event.preventDefault(); saveEmailTemplate(event); }); } } // Newsletter creation if (document.getElementById("newsletterForm")) { loadNewsletterStats(); } // Newsletter subscribers if (document.getElementById("subscribersTable")) { loadSubscribers(); } // Contact submissions if (document.getElementById("submissionsTable")) { loadSubmissions(); const filterForm = document.getElementById("filterForm"); if (filterForm) { filterForm.addEventListener("submit", (e) => { e.preventDefault(); submissionsCurrentPage = 1; loadSubmissions(); }); } // Table sorting document.querySelectorAll("th[data-sort]").forEach((header) => { header.addEventListener("click", function () { const sortBy = this.dataset.sort; if (submissionsSortBy === sortBy) { submissionsSortOrder = submissionsSortOrder === "asc" ? "desc" : "asc"; } else { submissionsSortBy = sortBy; submissionsSortOrder = "asc"; } submissionsCurrentPage = 1; loadSubmissions(); }); }); } // Settings if (document.getElementById("settingsList")) { loadSettingsForList(); } // Email settings const emailSettingsForm = document.getElementById("emailSettingsForm"); if (emailSettingsForm) { loadEmailSettings(); emailSettingsForm.addEventListener("submit", submitEmailSettings); } });