← Back
Editing: admin.js
/** * Admin JavaScript for SeedProd WordPress-native pages */ (function( $ ) { 'use strict'; $( document ).ready( function() { // Fix menu expansion for hidden pages (function() { var currentPage = new URLSearchParams(window.location.search).get('page'); var $seedprodMenu = $('#toplevel_page_seedprod_lite'); if (!$seedprodMenu.length) return; // Handle hidden pages that need menu expansion switch(currentPage) { case 'seedprod_lite_theme_kits_selection': // Expand menu and highlight Website Builder $seedprodMenu.removeClass('wp-not-current-submenu').addClass('wp-has-current-submenu wp-menu-open'); $seedprodMenu.find('.wp-submenu').show(); $seedprodMenu.find('a[href="admin.php?page=seedprod_lite_website_builder"]').parent().addClass('current'); break; case 'seedprod_lite_template_selection': // Expand menu and highlight Landing Pages $seedprodMenu.removeClass('wp-not-current-submenu').addClass('wp-has-current-submenu wp-menu-open'); $seedprodMenu.find('.wp-submenu').show(); $seedprodMenu.find('.wp-submenu li').removeClass('current'); $seedprodMenu.find('a[href="admin.php?page=seedprod_lite_landing_pages"]').parent().addClass('current'); break; } })(); // Handle notification dismissal $(document).on('click', '.seedprod-notification-dismiss', function(e) { e.preventDefault(); var $notification = $(this).closest('.seedprod-notification-bar'); var notificationId = $notification.data('id'); // Fade out the notification immediately for better UX $notification.fadeOut(300); // Make AJAX call to dismiss notification permanently $.post(seedprod_admin.ajax_url, { action: 'seedprod_lite_notification_dismiss', id: notificationId, _wpnonce: seedprod_admin.notification_dismiss_nonce }, function(response) { // Notification already hidden, no further action needed }).fail(function() { // If dismiss fails, show notification again $notification.fadeIn(300); }); }); // Dashboard-specific functionality (exclude landing pages) if ( $( '.seedprod-dashboard-page' ).length && ! $( '.seedprod-landing-pages-page' ).length ) { initDashboard(); } // Landing Pages-specific functionality if ( $( '.seedprod-landing-pages-page' ).length ) { initLandingPages(); } // Template Selection-specific functionality if ( $( '.seedprod-template-selection-page' ).length ) { initTemplateSelection(); } // Theme Kits Selection-specific functionality if ( $( '.seedprod-theme-kits-selection-page' ).length ) { initThemeKitsSelection(); } // Settings-specific functionality if ( $( '.seedprod-settings-page' ).length ) { initSettings(); } // Subscribers-specific functionality if ( $( '#seedprod-subscribers-tab' ).length ) { initSubscribers(); } // AI Themes external link handler $('a[href*="seedprod_lite_ai_themes"]').on('click', function(e) { e.preventDefault(); window.open(seedprod_admin.urls.ai_theme_builder, '_blank'); }); }); /** * Render a non-blocking warning notice listing images that could not be * sideloaded during a theme or landing-page import. No-op when the list * is empty so the happy-path UX is unchanged. * * @param {Array} warnings Array of { url, reason } objects from the import response. * @param {jQuery} $statusEl The .seedprod-import-status element to append the notice to. */ function seedprodRenderImportWarnings(warnings, $statusEl) { if (!warnings || !warnings.length || !$statusEl || !$statusEl.length) { return; } $statusEl.find('.seedprod-import-warnings').remove(); var heading = (seedprod_admin.strings && seedprod_admin.strings.import_warnings_heading) ? seedprod_admin.strings.import_warnings_heading : 'These images couldn\'t be imported and will not appear on your site:'; var $notice = $('<div class="notice notice-warning seedprod-import-warnings"></div>'); $notice.append($('<p></p>').text(heading)); var $list = $('<ul></ul>'); for (var i = 0; i < warnings.length; i++) { var entry = warnings[i] || {}; var url = entry.url || ''; var reason = entry.reason || ''; var $li = $('<li></li>').text(url); if (reason && reason !== 'download-failed') { $li.append(document.createTextNode(' (' + reason + ')')); } $list.append($li); } $notice.append($list); $statusEl.append($notice); $statusEl.show(); } /** * Initialize dashboard functionality */ function initDashboard() { // License activation handler $('#seedprod-license-form').on('submit', function(e) { e.preventDefault(); var $form = $(this); var $button = $form.find('button'); var $message = $('#seedprod-license-message'); var licenseKey = $('#seedprod-license-key').val(); if (!licenseKey) { $message.html('<div class="seedprod-message-error">' + seedprod_admin.strings.license_empty + '</div>'); return; } // Show loading state $button.prop('disabled', true); $button.find('.button-text').hide(); $button.find('.button-spinner').css('display', 'inline-block'); $message.empty(); // Make AJAX call to validate license $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_save_api_key', api_key: licenseKey, _wpnonce: seedprod_admin.nonce }, success: function(response) { if (response.status === 'true') { $message.html('<div class="seedprod-message-success">' + '<span class="dashicons dashicons-yes"></span> ' + seedprod_admin.strings.license_success + '</div>'); // Keep the button in success state $button.find('.button-spinner').hide(); $button.find('.button-text').show(); setTimeout(function() { // Reload with activation success parameter window.location.href = window.location.pathname + '?page=seedprod_lite&activated=true'; }, 1500); } else { var errorMsg = response.msg || seedprod_admin.strings.license_invalid; // Show WordPress admin notice for error var noticeClass = 'notice-error'; var $notice = $('<div class="notice ' + noticeClass + ' is-dismissible seedprod-admin-notice"><p>' + errorMsg + '</p></div>'); // Insert notice at top of dashboard container if ($('.seedprod-dashboard-container').length) { $('.seedprod-dashboard-container').prepend($notice); } else { // Fallback - insert after header $('.seedprod-header').after($notice); } // Make dismissible $notice.on('click', '.notice-dismiss', function() { $(this).remove(); }); // Also show inline error message $message.html('<div class="seedprod-message-error">' + '<span class="dashicons dashicons-warning"></span> ' + errorMsg + '</div>'); // Reset button on error $button.prop('disabled', false); $button.find('.button-spinner').hide(); $button.find('.button-text').show(); } }, error: function() { $message.html('<div class="seedprod-message-error">' + '<span class="dashicons dashicons-warning"></span> ' + seedprod_admin.strings.license_error + '</div>'); // Reset button on error $button.prop('disabled', false); $button.find('.button-spinner').hide(); $button.find('.button-text').show(); } }); }); // Toggle switches for enable/disable modes $('.seedprod-toggle').on('change', function() { var $toggle = $(this); var mode = $toggle.data('mode'); var isChecked = $toggle.is(':checked'); // Disable toggle during AJAX $toggle.prop('disabled', true); // Get current settings var settings = { enable_coming_soon_mode: $('.seedprod-toggle[data-mode="coming_soon"]').is(':checked'), enable_maintenance_mode: $('.seedprod-toggle[data-mode="maintenance"]').is(':checked'), enable_login_mode: $('.seedprod-toggle[data-mode="login"]').is(':checked'), enable_404_mode: $('.seedprod-toggle[data-mode="404"]').is(':checked') }; // Apply the change if (mode === 'coming_soon') { settings.enable_coming_soon_mode = isChecked; // If enabling coming soon, disable maintenance if (isChecked && settings.enable_maintenance_mode) { settings.enable_maintenance_mode = false; $('.seedprod-toggle[data-mode="maintenance"]').prop('checked', false); updateStatusBadge('maintenance', false); } } else if (mode === 'maintenance') { settings.enable_maintenance_mode = isChecked; // If enabling maintenance, disable coming soon if (isChecked && settings.enable_coming_soon_mode) { settings.enable_coming_soon_mode = false; $('.seedprod-toggle[data-mode="coming_soon"]').prop('checked', false); updateStatusBadge('coming_soon', false); } } else if (mode === 'login') { settings.enable_login_mode = isChecked; } else if (mode === '404') { settings.enable_404_mode = isChecked; } // Save settings via AJAX $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_save_settings', settings: JSON.stringify(settings), _wpnonce: seedprod_admin.nonce }, success: function(response) { // Update status badge updateStatusBadge(mode, isChecked); // Refresh page for coming soon and maintenance modes to update admin bar notice if (mode === 'coming_soon' || mode === 'maintenance') { // Show success state briefly before refresh setTimeout(function() { window.location.reload(); }, 500); } else { // Re-enable toggle for other modes $toggle.prop('disabled', false); } }, error: function() { // Revert toggle on error $toggle.prop('checked', !isChecked); $toggle.prop('disabled', false); alert(seedprod_admin.strings.settings_error); } }); }); // Copy to clipboard functionality $('#seedprod-copy-system-info').on('click', function(e) { e.preventDefault(); var $button = $(this); var textarea = document.getElementById('seedprod-system-info-text'); if (!textarea) return; // Try modern clipboard API first (works on HTTPS) if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(textarea.value).then(function() { $button.text(seedprod_admin.strings.copied || 'Copied!'); setTimeout(function() { $button.text(seedprod_admin.strings.copy_to_clipboard || 'Copy to Clipboard'); }, 2000); }).catch(function() { // Fallback to old method copyUsingExecCommand(); }); } else { // Use fallback for HTTP/localhost copyUsingExecCommand(); } function copyUsingExecCommand() { // Select the text textarea.select(); textarea.setSelectionRange(0, 99999); // For mobile devices try { // Copy using the old execCommand var successful = document.execCommand('copy'); if (successful) { $button.text(seedprod_admin.strings.copied || 'Copied!'); setTimeout(function() { $button.text(seedprod_admin.strings.copy_to_clipboard || 'Copy to Clipboard'); }, 2000); } } catch (err) { console.error('Failed to copy text: ', err); } // Deselect the text if (window.getSelection) { window.getSelection().removeAllRanges(); } } }); // Plugin installation/activation handlers $('.seedprod-plugin-button').on('click', function(e) { e.preventDefault(); var $button = $(this); var $item = $button.closest('.seedprod-plugin-item, .seedprod-plugin-card'); var pluginKey = $item.data('plugin'); var pluginSlug = $button.data('plugin-slug'); var pluginId = $button.data('plugin-id'); var status = parseInt($button.data('status')); // Prevent double clicks if ($button.prop('disabled')) { return; } // Show loading state (WordPress native spinner) $button.prop('disabled', true); // Remove any attention-grabbing indicators when user takes action $button.removeClass('seedprod-needs-activation'); var originalText = $button.text(); $button.data('original-text', originalText); // Change text and add WordPress native spinner var loadingText = ''; if (status === 0) { loadingText = seedprod_admin.strings.installing || 'Installing...'; } else if (status === 1) { loadingText = seedprod_admin.strings.deactivating || 'Deactivating...'; } else if (status === 2) { loadingText = seedprod_admin.strings.activating || 'Activating...'; } var spinner = $('<span class="spinner is-active" style="float: none; margin: 0 5px 0 0;"></span>'); $button.empty().append(spinner).append(loadingText); // Determine action based on status var action = ''; if (status === 0) { action = 'seedprod_lite_v2_install_plugin'; } else if (status === 1) { action = 'seedprod_lite_v2_deactivate_plugin'; } else if (status === 2) { action = 'seedprod_lite_v2_activate_plugin'; } // Prepare data - use plugin_id for install, plugin slug for activate/deactivate var data = { action: action, _wpnonce: seedprod_admin.nonce }; if (status === 0) { // Installing - send plugin ID (not URL for security) data.plugin_id = pluginId; } else { // Activating/deactivating - send plugin slug data.plugin = pluginSlug; } // Set timeout for stuck installations (30 seconds for install, 10 seconds for activate/deactivate) var timeoutDuration = (status === 0) ? 30000 : 10000; var requestTimeout = setTimeout(function() { // If we get here, the request is stuck // Reset button state $button.prop('disabled', false); $button.find('.spinner').remove(); $button.text(originalText); // Show timeout error var timeoutMsg = (status === 0) ? (seedprod_admin.strings.plugin_install_timeout || 'Installation is taking longer than expected. Please try again or check your server logs.') : (seedprod_admin.strings.plugin_timeout || 'Operation timed out. Please try again.'); showNotice('error', timeoutMsg); }, timeoutDuration); // Make AJAX request $.post(seedprod_admin.ajax_url, data, function(response) { // Clear the timeout since request completed clearTimeout(requestTimeout); if (response.success) { // Update button based on new status if (status === 0) { // Was not installed, now inactive - keep as primary button for activate $button.data('status', 2); // Update status text $item.find('.seedprod-plugin-status strong').text(seedprod_admin.strings.plugin_inactive); // Add attention-grabbing indicator since user needs to activate $button.addClass('seedprod-needs-activation'); // Remove the indicator after 10 seconds setTimeout(function() { $button.removeClass('seedprod-needs-activation'); }, 10000); showNotice('success', response.data.message || seedprod_admin.strings.plugin_installed); } else if (status === 1) { // Was active, now inactive - switch to primary button for activate $button.data('status', 2); $button.removeClass('seedprod-button-secondary').addClass('button-primary seedprod-button-primary'); // Update status text $item.find('.seedprod-plugin-status strong').text(seedprod_admin.strings.plugin_inactive); showNotice('success', response.data || seedprod_admin.strings.plugin_deactivated); // Reload page after deactivation to ensure UI is fully updated setTimeout(function() { window.location.reload(); }, 1000); } else if (status === 2) { // Was inactive, now active - switch to secondary button for deactivate $button.data('status', 1); $button.removeClass('button-primary seedprod-button-primary').addClass('seedprod-button-secondary'); // Update status text $item.find('.seedprod-plugin-status strong').text(seedprod_admin.strings.plugin_active); showNotice('success', response.data || seedprod_admin.strings.plugin_activated); // Reload page after activation to ensure UI is fully updated setTimeout(function() { window.location.reload(); }, 1000); } } else { var errorMsg = response.data || seedprod_admin.strings.plugin_error; showNotice('error', errorMsg); } }).fail(function() { // Clear the timeout since request completed (even if failed) clearTimeout(requestTimeout); showNotice('error', seedprod_admin.strings.plugin_network_error); }).always(function() { // Clear timeout in case it's still running clearTimeout(requestTimeout); // Reset button state (remove WordPress native spinner) $button.prop('disabled', false); $button.find('.spinner').remove(); // Update button text based on the new status var newStatus = $button.data('status'); var newText = ''; if (newStatus === 0) { newText = seedprod_admin.strings.plugin_install; } else if (newStatus === 1) { newText = seedprod_admin.strings.plugin_deactivate; } else if (newStatus === 2) { newText = seedprod_admin.strings.plugin_activate; } $button.text(newText); }); }); /** * Update status badge for toggles */ function updateStatusBadge(mode, isActive) { var $item = $('.seedprod-toggle[data-mode="' + mode + '"]').closest('.seedprod-setup-item'); var $badge = $item.find('.seedprod-status-badge'); var $icon = $item.find('.seedprod-setup-icon'); if ($badge.length === 0) { // Create badge if it doesn't exist $badge = $('<span class="seedprod-status-badge"></span>'); $item.find('.seedprod-setup-item-controls').append($badge); } if (isActive) { $badge.removeClass('seedprod-status-inactive') .addClass('seedprod-status-active') .text(seedprod_admin.strings.status_active); $icon.addClass('active'); } else { $badge.removeClass('seedprod-status-active') .addClass('seedprod-status-inactive') .text(seedprod_admin.strings.status_inactive); $icon.removeClass('active'); } } /** * Helper function to show notices */ function showNotice(type, message) { var noticeClass = type === 'success' ? 'notice-success' : 'notice-error'; var $notice = $('<div class="notice ' + noticeClass + ' is-dismissible seedprod-admin-notice"><p>' + message + '</p></div>'); // Insert notice in appropriate container based on page if ($('.seedprod-dashboard-container').length) { // Dashboard page $('.seedprod-dashboard-container').prepend($notice); } else if ($('.seedprod-settings-container').length) { // Settings page $('.seedprod-settings-container').prepend($notice); } else { // Fallback - insert after any SeedProd header $('.seedprod-header').after($notice); } // Auto dismiss after 5 seconds setTimeout(function() { $notice.fadeOut(function() { $(this).remove(); }); }, 5000); // Make dismissible $notice.on('click', '.notice-dismiss', function() { $notice.fadeOut(function() { $(this).remove(); }); }); // Add dismiss button if not present if (!$notice.find('.notice-dismiss').length) { $notice.append('<button type="button" class="notice-dismiss"><span class="screen-reader-text">' + seedprod_admin.strings.dismiss_notice + '</span></button>'); } } } /** * Initialize theme kits selection functionality */ function initThemeKitsSelection() { var themeKitsState = { currentTab: 'all-templates', search: '', filter: 'all', sort: '', themes: {}, favorites: [], currentPage: 1, totalPages: 1, searchTimeout: null }; // Initialize based on current tab if (typeof seedprodThemeKitsData !== 'undefined') { themeKitsState.currentTab = seedprodThemeKitsData.activeTab || 'all-templates'; } // Load themes immediately (we're already in document ready) if (themeKitsState.currentTab === 'all-templates') { loadThemes(); } else if (themeKitsState.currentTab === 'favorite-templates') { loadFavoriteThemes(); } // Search functionality with debouncing $('#seedprod-template-search').on('keyup', function() { var searchTerm = $(this).val(); themeKitsState.search = searchTerm; // When searching, reset filter to 'all' to search across all themes if (searchTerm && themeKitsState.filter !== 'all') { themeKitsState.filter = 'all'; $('.seedprod-filter-pill').removeClass('active'); $('.seedprod-filter-pill[data-filter="all"]').addClass('active'); } // Clear existing timeout if (themeKitsState.searchTimeout) { clearTimeout(themeKitsState.searchTimeout); } // Set new timeout for 300ms delay themeKitsState.searchTimeout = setTimeout(function() { themeKitsState.currentPage = 1; if (themeKitsState.currentTab === 'all-templates') { loadThemes(); } else if (themeKitsState.currentTab === 'favorite-templates') { loadFavoriteThemes(); } }, 300); }); // Filter functionality $('.seedprod-filter-pill').on('click', function() { var filter = $(this).data('filter'); themeKitsState.filter = filter; themeKitsState.currentPage = 1; // Clear search when selecting a filter themeKitsState.search = ''; $('#seedprod-template-search').val(''); // Update active states $('.seedprod-filter-pill').removeClass('active'); $(this).addClass('active'); loadThemes(); }); // Sort functionality $('#seedprod-theme-sort').on('change', function() { themeKitsState.sort = $(this).val(); themeKitsState.currentPage = 1; loadThemes(); }); // Pagination functionality $('#seedprod-first-page').on('click', function() { if (!$(this).prop('disabled')) { themeKitsState.currentPage = 1; loadThemes(); } }); $('#seedprod-prev-page').on('click', function() { if (!$(this).prop('disabled') && themeKitsState.currentPage > 1) { themeKitsState.currentPage--; loadThemes(); } }); $('#seedprod-next-page').on('click', function() { if (!$(this).prop('disabled') && themeKitsState.currentPage < themeKitsState.totalPages) { themeKitsState.currentPage++; loadThemes(); } }); $('#seedprod-last-page').on('click', function() { if (!$(this).prop('disabled')) { themeKitsState.currentPage = themeKitsState.totalPages; loadThemes(); } }); // Template hover effects (reuse the same hover logic as landing pages) $(document).on('mouseenter', '.seedprod-theme-kits-grid .seedprod-template-card', function() { $(this).find('.seedprod-template-overlay').show(); }); $(document).on('mouseleave', '.seedprod-theme-kits-grid .seedprod-template-card', function() { // Don't hide overlay if card is in processing state if (!$(this).hasClass('coming-sooncessing')) { $(this).find('.seedprod-template-overlay').hide(); } }); // Theme selection (import) $(document).on('click', '.seedprod-theme-kits-grid .seedprod-template-select', function(e) { e.preventDefault(); var $button = $(this); var themeId = $(this).data('template-id'); var themeName = $(this).data('template-name'); // Get total theme pages count first $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_get_total_theme_pages', nonce: seedprod_admin.v2_nonce }, success: function(response) { var totalThemePages = response.data ? response.data.count : 0; // If there are existing theme pages, ask if they want to delete them if (totalThemePages > 0) { if (confirm(seedprod_admin.strings.theme_import_confirm.replace('%d', totalThemePages))) { // Delete existing theme pages first deleteThemePages(function() { // Then import the new theme importTheme(themeId, themeName, $button); }); } else { // User cancelled - do nothing return; } } else { // No existing pages, just import importTheme(themeId, themeName, $button); } }, error: function() { // On error, just try to import anyway importTheme(themeId, themeName, $button); } }); }); // Function to delete theme pages function deleteThemePages(callback) { $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_delete_theme_pages', nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { if (callback) callback(); } else { alert(seedprod_admin.strings.theme_delete_error.replace('%s', response.data || seedprod_admin.strings.unknown_error)); } }, error: function() { alert(seedprod_admin.strings.theme_delete_general_error); } }); } // Function to import theme function importTheme(themeId, themeName, $button) { var $templateCard = $button.closest('.seedprod-template-card'); var $overlay = $button.closest('.seedprod-template-overlay'); // Keep overlay visible during processing $templateCard.addClass('coming-sooncessing'); $overlay.addClass('coming-sooncessing-active'); // Show loading state $button.prop('disabled', true); $button.find('.dashicons').removeClass('dashicons-yes').addClass('dashicons-update').addClass('seedprod-spin'); // Show importing message showNotice(seedprod_admin.strings.theme_import_starting.replace('%s', themeName), 'info'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_import_theme_request', theme_id: themeId, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { showNotice(seedprod_admin.strings.theme_import_success.replace('%s', themeName), 'success'); var warnings = (response.data && response.data.warnings) ? response.data.warnings : []; var $warningHost = $('.seedprod-dashboard-container').first(); if (warnings.length && $warningHost.length) { seedprodRenderImportWarnings(warnings, $warningHost); } // Redirect to theme templates page after short delay; hold longer when warnings shown. var redirectDelay = warnings.length ? 8000 : 2000; setTimeout(function() { window.location.href = seedprod_admin.admin_url + 'admin.php?page=seedprod_lite_website_builder'; }, redirectDelay); } else { showNotice(seedprod_admin.strings.theme_import_error.replace('%s', response.data || seedprod_admin.strings.unknown_error), 'error'); // Reset button state $templateCard.removeClass('coming-sooncessing'); $overlay.removeClass('coming-sooncessing-active'); $button.prop('disabled', false); $button.find('.dashicons').removeClass('dashicons-update seedprod-spin').addClass('dashicons-yes'); } }, error: function() { showNotice(seedprod_admin.strings.template_load_error, 'error'); // Reset button state $templateCard.removeClass('coming-sooncessing'); $overlay.removeClass('coming-sooncessing-active'); $button.prop('disabled', false); $button.find('.dashicons').removeClass('dashicons-update seedprod-spin').addClass('dashicons-yes'); } }); } // Theme preview $(document).on('click', '.seedprod-theme-kits-grid .seedprod-template-preview', function(e) { e.preventDefault(); var themeId = $(this).data('template-id'); // Open preview in new tab (matching Vue.js implementation) window.open('https://preview.seedprod.com/' + themeId, '_blank'); // Commented out iframe modal implementation for now // var themeName = $(this).data('template-name') || 'Theme Kit Preview'; // previewThemeKit(themeId, themeName); }); // Close preview modal for theme kits - commented out for now // $(document).on('click', '#seedprod-template-preview-modal .seedprod-modal-close, #seedprod-template-preview-modal .seedprod-modal-overlay', function() { // // Check if we're on theme kits page // if ($('.seedprod-theme-kits-selection-page').length > 0) { // closeThemePreviewModal(); // } // }); // Favorite toggle (matching landing page templates) $(document).on('click', '.seedprod-theme-kits-grid .seedprod-favorite-toggle', function(e) { e.preventDefault(); e.stopPropagation(); var $toggle = $(this); var templateId = $toggle.data('template-id'); var isFavorited = $toggle.hasClass('seedprod-favorited'); var method = isFavorited ? 'detach' : 'attach'; // Optimistically update UI $toggle.toggleClass('seedprod-favorited'); // Save favorite state via AJAX $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_toggle_favorite_theme', template_id: templateId, method: method, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (!response.success) { // Revert on error $toggle.toggleClass('seedprod-favorited'); console.error('Failed to toggle favorite:', response.data); } }, error: function() { // Revert on error $toggle.toggleClass('seedprod-favorited'); console.error('Failed to toggle favorite'); } }); }); /** * Load themes from API */ function loadThemes() { var $grid = $('#all-themes-grid'); showLoadingSpinner($grid); // Map filter to category ID (10 for WooCommerce) var categoryId = themeKitsState.filter === 'woocommerce' ? '10' : ''; // Build request data var requestData = { action: 'seedprod_lite_v2_get_theme_kits', nonce: seedprodThemeKitsData.nonce, page: themeKitsState.currentPage, filter: 'themes', category: categoryId, search: themeKitsState.search, sort: themeKitsState.sort }; $.ajax({ url: seedprodThemeKitsData.ajaxUrl, type: 'POST', data: requestData, success: function(response) { if (response.success && response.data) { // For All Themes, pagination info is in data.templates if (response.data.templates) { themeKitsState.themes = response.data.templates; renderThemes(response.data, false); // false = not favorites tab updatePagination(response.data.templates); // Pass templates object with pagination } else { // Fallback for unexpected structure themeKitsState.themes = response.data; renderThemes(response.data, false); updatePagination(response.data); } } else { showError($grid, response.data || seedprod_admin.strings.error_loading_templates); } }, error: function() { showError($grid, seedprod_admin.strings.error_loading_templates); } }); } /** * Load favorite themes from API */ function loadFavoriteThemes() { var $grid = $('#favorite-themes-grid'); showLoadingSpinner($grid); var requestData = { action: 'seedprod_lite_v2_get_theme_kits', nonce: seedprodThemeKitsData.nonce, page: themeKitsState.currentPage, filter: 'favorites', search: themeKitsState.search }; $.ajax({ url: seedprodThemeKitsData.ajaxUrl, type: 'POST', data: requestData, success: function(response) { if (response.success && response.data) { // For favorites, the API returns themes in data.data (like Vue component line 1126) var data = response.data; // Check if we have themes in the expected location if (data.data && Array.isArray(data.data)) { if (data.data.length > 0) { // Mark all as favorited (like Vue component line 1127-1129) data.data.forEach(function(v) { v.favorited = true; }); } // Always render, even if empty renderThemes(data, true); updatePagination(data); } else { // Show empty state renderThemes({data: []}, true); updatePagination({data: [], current_page: 1, last_page: 1}); } } else { showError($grid, response.data || seedprod_admin.strings.error_loading_templates); } }, error: function() { showError($grid, seedprod_admin.strings.error_loading_templates); } }); } /** * Render themes in the grid */ function renderThemes(data, isFavorites) { var gridId = isFavorites ? '#favorite-themes-grid' : '#all-themes-grid'; var $grid = $(gridId); $grid.empty(); // Handle response structure exactly like Vue component var themes = []; var favs = []; if (isFavorites === true) { // Favorites tab: themes come from data.data themes = data.data || []; // These are already favorited (set in loadFavoriteThemes) } else { // All themes tab: exactly like Vue component (line 1074-1079) if (data && data.templates && data.templates.data) { themes = data.templates.data; favs = data.favs || []; // Set favorited exactly like Vue component does (line 1077-1079) themes.forEach(function(v) { v.favorited = favs.includes(v.id); }); } else if (data && data.templates) { themes = data.templates; favs = data.favs || []; themes.forEach(function(v) { v.favorited = favs.includes(v.id); }); } } if (!themes || themes.length === 0) { $grid.html('<div class="seedprod-no-templates">' + '<p>' + seedprod_admin.strings.no_templates_found + '</p></div>'); $('.seedprod-themes-pagination').hide(); return; } // Create theme cards themes.forEach(function(theme) { var card = createThemeCard(theme); $grid.append(card); }); // Show pagination $('.seedprod-themes-pagination').show(); } /** * Create a theme card element */ function createThemeCard(theme) { var templateId = theme.id; var templateName = theme.name || seedprod_admin.strings.untitled_theme; // Use the favorited property directly - it should be a boolean var isFavorited = !!theme.favorited; // Convert to boolean var heartClass = isFavorited ? 'seedprod-favorited' : ''; // Build image URL same as Vue version var imageUrl = 'https://assets.seedprod.com/themes/preview-' + templateId + '.jpg'; // Build favorite icon HTML (EXACTLY matching landing page templates) var actionIcon = '<span class="seedprod-favorite-toggle ' + heartClass + '" data-template-id="' + templateId + '">' + '<span class="dashicons dashicons-heart"></span>' + '</span>'; return '<div class="seedprod-template-card" data-template-id="' + templateId + '">' + '<div class="seedprod-template-thumbnail">' + '<div class="seedprod-theme-image">' + '<img src="' + imageUrl + '" alt="' + templateName + '" class="seedprod-template-image" loading="lazy">' + '</div>' + '<div class="seedprod-template-overlay" style="display: none;">' + '<button class="seedprod-template-select" data-template-id="' + templateId + '" data-template-name="' + templateName + '">' + '<span class="dashicons dashicons-yes"></span>' + '</button>' + '<button class="seedprod-template-preview" data-template-id="' + templateId + '" data-template-name="' + templateName + '">' + '<span class="dashicons dashicons-search"></span>' + '</button>' + '</div>' + '</div>' + '<div class="seedprod-template-name">' + '<span class="template-name">' + templateName + '</span>' + actionIcon + '</div>' + '</div>'; } /** * Update pagination controls */ function updatePagination(data) { themeKitsState.totalPages = data.last_page || 1; themeKitsState.currentPage = data.current_page || 1; $('#seedprod-current-page').text(themeKitsState.currentPage); $('#seedprod-total-pages').text(themeKitsState.totalPages); // Update button states $('#seedprod-first-page, #seedprod-prev-page').prop('disabled', themeKitsState.currentPage <= 1); $('#seedprod-next-page, #seedprod-last-page').prop('disabled', themeKitsState.currentPage >= themeKitsState.totalPages); } /** * Show loading spinner */ function showLoadingSpinner($container) { $container.html('<div class="seedprod-templates-loading">' + '<span class="spinner is-active"></span>' + '<p>' + seedprod_admin.strings.loading_templates + '</p></div>'); } /** * Show error message */ function showError($container, message) { $container.html('<div class="seedprod-error">' + '<p>' + message + '</p></div>'); } // Commented out iframe modal functions - keeping for potential future use // /** // * Preview theme kit in modal // */ // function previewThemeKit(themeId, themeName) { // // Use preview.seedprod.com URL for theme kits (as used in Vue.js version) // var previewUrl = 'https://preview.seedprod.com/' + themeId; // // // Update title // $('#seedprod-preview-title').text(themeName); // // // Show modal first // $('#seedprod-template-preview-modal').show(); // // // Add loading state to modal body // var $modalBody = $('#seedprod-template-preview-modal .seedprod-modal-body'); // var $iframe = $('#seedprod-preview-iframe'); // // // Hide iframe initially and show loading spinner // $iframe.hide(); // // // Add loading spinner if not already present // if (!$modalBody.find('.seedprod-preview-loading').length) { // $modalBody.append('<div class="seedprod-preview-loading" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; z-index: 10;">' + // '<span class="spinner is-active" style="float: none; margin: 0 auto 10px;"></span>' + // '<p>Loading preview...</p>' + // '</div>'); // } else { // $modalBody.find('.seedprod-preview-loading').show(); // } // // // Handle iframe load completion // $iframe.off('load').on('load', function() { // // Hide loading spinner // $modalBody.find('.seedprod-preview-loading').hide(); // // Show iframe with fade-in effect // $iframe.fadeIn(200); // }); // // // Set iframe source (this triggers loading) // $iframe.attr('src', previewUrl); // } // // /** // * Close preview modal // */ // function closeThemePreviewModal() { // $('#seedprod-template-preview-modal').hide(); // // Reset iframe to prevent flash of old content on next open // var $iframe = $('#seedprod-preview-iframe'); // $iframe.attr('src', ''); // $iframe.hide(); // // Hide any loading spinner // $('#seedprod-template-preview-modal .seedprod-preview-loading').hide(); // } } /** * Initialize settings page functionality */ function initSettings() { // License key handlers $('#seedprod-verify-license, #seedprod-recheck-license').on('click', function(e) { e.preventDefault(); saveLicenseKey(); }); $('#seedprod-deactivate-license').on('click', function(e) { e.preventDefault(); deactivateLicenseKey(); }); // Settings form handler $('#seedprod-settings-form').on('submit', function(e) { e.preventDefault(); saveAppSettings(); }); // System info toggle $('#seedprod-toggle-system-info').on('click', function() { var $systemInfo = $('#seedprod-system-info'); var $icon = $(this).find('.dashicons'); if ($systemInfo.is(':visible')) { $systemInfo.slideUp(); $icon.removeClass('dashicons-arrow-up-alt2').addClass('dashicons-arrow-down-alt2'); } else { $systemInfo.slideDown(); $icon.removeClass('dashicons-arrow-down-alt2').addClass('dashicons-arrow-up-alt2'); } }); } /** * Save/verify license key */ function saveLicenseKey() { var $button = $('#seedprod-verify-license, #seedprod-recheck-license'); var $message = $('#seedprod-license-message'); var licenseKey = $('#seedprod-license-key').val(); if (!licenseKey) { showInlineNotice($message, 'error', seedprod_admin.strings.license_empty || 'Please enter a license key.'); return; } // Disable button and show loading $button.prop('disabled', true).text(seedprod_admin.strings.verifying); $message.empty(); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_save_api_key', api_key: licenseKey, _wpnonce: seedprod_admin.nonce }, success: function(response) { $button.prop('disabled', false); // Check response.status (string) not response.success (boolean) if (response.status === 'true') { showInlineNotice($message, 'success', response.msg || seedprod_admin.strings.license_verify_success); // Reload page to show updated license status setTimeout(function() { location.reload(); }, 1500); } else { $button.text(seedprod_admin.strings.verify_key); showInlineNotice($message, 'error', response.msg || seedprod_admin.strings.license_invalid); // Hide the active badge if it exists $('.seedprod-license-badge-active').hide(); } }, error: function() { $button.prop('disabled', false).text(seedprod_admin.strings.verify_key); showInlineNotice($message, 'error', seedprod_admin.strings.license_error); // Hide the active badge if it exists $('.seedprod-license-badge-active').hide(); } }); } /** * Deactivate license key */ function deactivateLicenseKey() { var $button = $('#seedprod-deactivate-license'); var $message = $('#seedprod-license-message'); var licenseKey = $('#seedprod-license-key').val(); if (!confirm(seedprod_admin.strings.license_deactivate_confirm)) { return; } $button.prop('disabled', true).text(seedprod_admin.strings.license_deactivating); $message.empty(); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_deactivate_api_key', api_key: licenseKey, _wpnonce: seedprod_admin.nonce }, success: function(response) { $button.prop('disabled', false).text(seedprod_admin.strings.deactivate_key); if (response.success) { showInlineNotice($message, 'success', response.data.message || seedprod_admin.strings.license_deactivate_success); // Clear the license field and reload $('#seedprod-license-key').val(''); setTimeout(function() { location.reload(); }, 1500); } else { showInlineNotice($message, 'error', response.data.message || seedprod_admin.strings.license_deactivate_error); } }, error: function() { $button.prop('disabled', false).text(seedprod_admin.strings.deactivate_key); showInlineNotice($message, 'error', seedprod_admin.strings.license_error); } }); } /** * Save app settings */ function saveAppSettings() { var $button = $('#seedprod-save-settings'); // Remove any existing notices $('.seedprod-dashboard-container > .notice').remove(); // Collect form data var appSettings = { facebook_g_app_id: $('#seedprod-facebook-app-id').val(), google_places_app_key: $('#seedprod-google-places-key').val(), yelp_app_api_key: $('#seedprod-yelp-api-key').val(), disable_seedprod_button: $('#seedprod-disable-button').is(':checked'), enable_usage_tracking: $('#seedprod-usage-tracking').is(':checked'), disable_seedprod_notification: $('#seedprod-disable-notifications').is(':checked') }; $button.prop('disabled', true).text(seedprod_admin.strings.saving); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_save_app_settings', app_settings: appSettings, _wpnonce: $('#seedprod_settings_nonce').val() }, success: function(response) { $button.prop('disabled', false).text(seedprod_admin.strings.save_settings); if (response.success) { // Show success notice above tabs, below header var $notice = $('<div class="notice notice-success is-dismissible seedprod-notice-compact"><p>' + (response.data.message || seedprod_admin.strings.settings_save_success) + '</p></div>'); $('.seedprod-dashboard-container').prepend($notice); // Add dismiss button functionality $notice.on('click', '.notice-dismiss', function() { $notice.fadeOut(function() { $(this).remove(); }); }); // Add dismiss button if WordPress didn't add it if (!$notice.find('.notice-dismiss').length) { $notice.append('<button type="button" class="notice-dismiss"><span class="screen-reader-text">' + (seedprod_admin.strings.dismiss_notice || 'Dismiss this notice.') + '</span></button>'); } // Scroll to notice to ensure it's visible // Try to scroll to the page top, accounting for WP admin bar if ($('.seedprod-dashboard-page').length) { var scrollTarget = Math.max(0, $('.seedprod-dashboard-page').offset().top - 32); window.scrollTo({ top: scrollTarget, behavior: 'smooth' }); } else { // Fallback to simple scroll to top window.scrollTo({ top: 0, behavior: 'smooth' }); } // Auto-remove after 3 seconds setTimeout(function() { $notice.fadeOut(function() { $(this).remove(); }); }, 3000); } else { var $notice = $('<div class="notice notice-error is-dismissible seedprod-notice-compact"><p>' + (response.data.message || seedprod_admin.strings.settings_save_error) + '</p></div>'); $('.seedprod-dashboard-container').prepend($notice); // Add dismiss button functionality $notice.on('click', '.notice-dismiss', function() { $notice.fadeOut(function() { $(this).remove(); }); }); // Add dismiss button if WordPress didn't add it if (!$notice.find('.notice-dismiss').length) { $notice.append('<button type="button" class="notice-dismiss"><span class="screen-reader-text">' + (seedprod_admin.strings.dismiss_notice || 'Dismiss this notice.') + '</span></button>'); } } }, error: function() { $button.prop('disabled', false).text(seedprod_admin.strings.save_settings); var $notice = $('<div class="notice notice-error is-dismissible seedprod-notice-compact"><p>' + (seedprod_admin.strings.error_occurred || 'An error occurred. Please try again.') + '</p></div>'); $('.seedprod-dashboard-container').prepend($notice); // Add dismiss button functionality $notice.on('click', '.notice-dismiss', function() { $notice.fadeOut(function() { $(this).remove(); }); }); // Add dismiss button if WordPress didn't add it if (!$notice.find('.notice-dismiss').length) { $notice.append('<button type="button" class="notice-dismiss"><span class="screen-reader-text">' + (seedprod_admin.strings.dismiss_notice || 'Dismiss this notice.') + '</span></button>'); } } }); } /** * Helper function to show inline notices */ function showInlineNotice($element, type, message) { var noticeClass = type === 'success' ? 'notice-success' : 'notice-error'; $element.html('<div class="notice inline ' + noticeClass + '"><p>' + message + '</p></div>'); } /** * Initialize subscribers functionality */ function initSubscribers() { var subscribersTable = { currentPage: 1, perPage: 25, sortBy: 'created', sortOrder: 'desc', search: '', pageFilter: 'all', chartInstance: null }; // Load subscribers when tab becomes active if ($('#seedprod-subscribers-tab').is(':visible')) { loadSubscribers(); } // Reload when tab is clicked (if using tab navigation) $('.nav-tab').on('click', function() { if ($(this).attr('href').indexOf('subscribers') !== -1) { setTimeout(loadSubscribers, 100); } }); // Search functionality $('#seedprod-subscriber-search-btn').on('click', function() { performSearch(); }); $('#seedprod-subscriber-search').on('keypress', function(e) { if (e.which === 13) { e.preventDefault(); performSearch(); } }); $('#seedprod-subscriber-clear-search').on('click', function() { $('#seedprod-subscriber-search').val(''); subscribersTable.search = ''; subscribersTable.currentPage = 1; $('#seedprod-subscriber-clear-search').hide(); loadSubscribers(); }); // Filter functionality $('#seedprod-subscriber-page-filter').on('change', function() { subscribersTable.pageFilter = $(this).val(); subscribersTable.currentPage = 1; loadSubscribers(); }); // Sort functionality $('#seedprod-subscribers-table th.sortable a').on('click', function(e) { e.preventDefault(); var newSortBy = $(this).data('sort'); if (subscribersTable.sortBy === newSortBy) { subscribersTable.sortOrder = subscribersTable.sortOrder === 'desc' ? 'asc' : 'desc'; } else { subscribersTable.sortBy = newSortBy; subscribersTable.sortOrder = 'desc'; } subscribersTable.currentPage = 1; loadSubscribers(); }); // Select all functionality $(document).on('change', '#seedprod-subscriber-select-all', function() { var isChecked = $(this).is(':checked'); $('#seedprod-subscribers-list input[type="checkbox"]').prop('checked', isChecked); }); $(document).on('change', '.seedprod-subscriber-select-all-bottom', function() { var isChecked = $(this).is(':checked'); $('#seedprod-subscribers-list input[type="checkbox"]').prop('checked', isChecked); $('#seedprod-subscriber-select-all').prop('checked', isChecked); }); // Individual checkbox changes $(document).on('change', '#seedprod-subscribers-list input[type="checkbox"]', function() { var totalCheckboxes = $('#seedprod-subscribers-list input[type="checkbox"]').length; var checkedCheckboxes = $('#seedprod-subscribers-list input[type="checkbox"]:checked').length; $('#seedprod-subscriber-select-all, .seedprod-subscriber-select-all-bottom').prop('checked', totalCheckboxes === checkedCheckboxes); }); // Bulk actions $('#seedprod-subscriber-bulk-apply').on('click', function() { var action = $('#seedprod-subscriber-bulk-action').val(); var selectedIds = []; $('#seedprod-subscribers-list input[type="checkbox"]:checked').each(function() { selectedIds.push($(this).val()); }); if (!selectedIds.length) { alert(seedprod_admin.strings.subscriber_no_selection); return; } if (action === 'delete') { deleteSubscribers(selectedIds); } }); // Individual delete actions $(document).on('click', '.seedprod-delete-subscriber', function(e) { e.preventDefault(); var subscriberId = $(this).data('id'); deleteSubscribers([subscriberId]); }); // Export functionality $('#seedprod-export-subscribers').on('click', function() { exportSubscribers(); }); // Pagination $(document).on('click', '.seedprod-pagination-link', function(e) { e.preventDefault(); var page = parseInt($(this).data('page')); if (page && page !== subscribersTable.currentPage) { subscribersTable.currentPage = page; loadSubscribers(); } }); /** * Perform search */ function performSearch() { var searchTerm = $('#seedprod-subscriber-search').val().trim(); subscribersTable.search = searchTerm; subscribersTable.currentPage = 1; if (searchTerm) { $('#seedprod-subscriber-clear-search').show(); } else { $('#seedprod-subscriber-clear-search').hide(); } loadSubscribers(); } /** * Load subscribers data */ function loadSubscribers() { var $loading = $('#seedprod-subscribers-list'); $loading.html('<tr><td colspan="5" class="seedprod-loading-message"><span class="dashicons dashicons-update spin"></span> ' + seedprod_admin.strings.loading + '</td></tr>'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_get_subscribers_datatable', page: subscribersTable.currentPage, per_page: subscribersTable.perPage, search: subscribersTable.search, page_filter: subscribersTable.pageFilter, sort_by: subscribersTable.sortBy, sort_order: subscribersTable.sortOrder, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { displaySubscribers(response.data); } else { $loading.html('<tr><td colspan="5" class="seedprod-error-message">' + seedprod_admin.strings.error + ': ' + (response.data || seedprod_admin.strings.subscriber_load_error || 'Could not load subscribers.') + '</td></tr>'); } }, error: function() { $loading.html('<tr><td colspan="5" class="seedprod-error-message">' + (seedprod_admin.strings.network_error || 'Network error. Please try again.') + '</td></tr>'); } }); } /** * Display subscribers in table */ function displaySubscribers(data) { var html = ''; if (data.subscribers.length > 0) { data.subscribers.forEach(function(subscriber) { html += '<tr>'; html += '<th scope="row" class="check-column"><input type="checkbox" value="' + subscriber.id + '" /></th>'; html += '<td class="column-email"><strong>' + escapeHtml(subscriber.email) + '</strong></td>'; html += '<td class="column-name">' + escapeHtml(subscriber.name) + '</td>'; html += '<td class="column-created">' + subscriber.created + '</td>'; html += '<td class="column-actions">'; html += '<button type="button" class="button button-small seedprod-delete-subscriber" data-id="' + subscriber.id + '">' + (seedprod_admin.strings.delete || 'Delete') + '</button>'; html += '</td>'; html += '</tr>'; }); } // Add OptinMonster promo row if not active if (window.seedprodOptinMonster && !window.seedprodOptinMonster.active) { html += '<tr class="seedprod-optinmonster-row">'; html += '<td class="column-cb"></td>'; // Empty checkbox column html += '<td class="column-email" colspan="2">'; html += '<span class="dashicons dashicons-megaphone" style="color: #087ce1; margin-right: 5px;"></span>'; html += '<strong style="color: #087ce1;">Pro Tip:</strong> '; html += 'Get 3x more subscribers with OptinMonster\'s exit-intent popups'; html += '</td>'; html += '<td class="column-created">'; html += '<a href="https://optinmonster.com/?utm_source=seedprod&utm_medium=subscribers-table&utm_campaign=cross-sell" target="_blank" style="color: #087ce1;">Learn more</a>'; html += '</td>'; html += '<td class="column-actions">'; var optinMonsterUrl = seedprod_admin.admin_url + 'admin.php?page=seedprod_lite_settings&tab=recommended-plugins&filter=optinmonster'; if (!window.seedprodOptinMonster.installed) { html += '<a href="' + optinMonsterUrl + '" class="button button-small button-primary">Install Free</a>'; } else { html += '<a href="' + optinMonsterUrl + '" class="button button-small button-primary">Activate</a>'; } html += '</td>'; html += '</tr>'; } $('#seedprod-subscribers-list').html(html); // Update count $('#seedprod-subscriber-count').text(data.total); // Update pagination updatePagination(data); // Update sort indicators updateSortIndicators(); } /** * Update pagination */ function updatePagination(data) { var html = ''; if (data.pages > 1) { // Previous page if (subscribersTable.currentPage > 1) { html += '<a class="prev-page button seedprod-pagination-link" data-page="' + (subscribersTable.currentPage - 1) + '">‹</a> '; } else { html += '<span class="tablenav-pages-navspan button disabled">‹</span> '; } // Page numbers var startPage = Math.max(1, subscribersTable.currentPage - 2); var endPage = Math.min(data.pages, subscribersTable.currentPage + 2); if (startPage > 1) { html += '<a class="page-numbers seedprod-pagination-link" data-page="1">1</a> '; if (startPage > 2) { html += '<span class="page-numbers dots">…</span> '; } } for (var i = startPage; i <= endPage; i++) { if (i === subscribersTable.currentPage) { html += '<span class="page-numbers current">' + i + '</span> '; } else { html += '<a class="page-numbers seedprod-pagination-link" data-page="' + i + '">' + i + '</a> '; } } if (endPage < data.pages) { if (endPage < data.pages - 1) { html += '<span class="page-numbers dots">…</span> '; } html += '<a class="page-numbers seedprod-pagination-link" data-page="' + data.pages + '">' + data.pages + '</a> '; } // Next page if (subscribersTable.currentPage < data.pages) { html += '<a class="next-page button seedprod-pagination-link" data-page="' + (subscribersTable.currentPage + 1) + '">›</a>'; } else { html += '<span class="tablenav-pages-navspan button disabled">›</span>'; } } $('#seedprod-subscriber-pagination').html(html); } /** * Update sort indicators */ function updateSortIndicators() { $('#seedprod-subscribers-table th.sortable').removeClass('asc desc'); var $currentSort = $('#seedprod-subscribers-table th.sortable a[data-sort="' + subscribersTable.sortBy + '"]').parent(); $currentSort.addClass(subscribersTable.sortOrder); } /** * Delete subscribers */ function deleteSubscribers(ids) { if (!confirm(seedprod_admin.strings.subscriber_delete_confirm)) { return; } $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_delete_subscribers', ids: ids, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { showNotice($('.seedprod-dashboard-container'), 'success', response.data.message || seedprod_admin.strings.subscriber_delete_success); loadSubscribers(); // Reload table } else { showNotice($('.seedprod-dashboard-container'), 'error', response.data || seedprod_admin.strings.subscriber_error); } }, error: function() { showNotice($('.seedprod-dashboard-container'), 'error', seedprod_admin.strings.subscriber_error); } }); } /** * Export subscribers to CSV */ function exportSubscribers() { var $button = $('#seedprod-export-subscribers'); var originalText = $button.text(); $button.prop('disabled', true).text(seedprod_admin.strings.exporting); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_export_subscribers', page_filter: subscribersTable.pageFilter, nonce: seedprod_admin.v2_nonce }, success: function(response) { $button.prop('disabled', false).text(originalText); if (response.success) { // Create download link var blob = new Blob([response.data.csv], { type: 'text/csv' }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = response.data.filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); showNotice($('.seedprod-dashboard-container'), 'success', response.data.message || seedprod_admin.strings.subscriber_export_success); } else { showNotice($('.seedprod-dashboard-container'), 'error', response.data || seedprod_admin.strings.template_import_error); } }, error: function() { $button.prop('disabled', false).text(originalText); showNotice($('.seedprod-dashboard-container'), 'error', seedprod_admin.strings.template_import_error); } }); } /** * Escape HTML for safe display */ function escapeHtml(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } } /** * Initialize landing pages functionality */ function initLandingPages() { // Toggle switches for enable/disable modes $('.seedprod-landing-pages-page .seedprod-toggle').on('change', function() { var $toggle = $(this); var mode = $toggle.data('mode'); var isChecked = $toggle.is(':checked'); // Don't process pro features in lite mode if ($toggle.closest('.coming-soon-feature').length && $('.seedprod-lite').length) { $toggle.prop('checked', false); return false; } // Disable toggle during AJAX $toggle.prop('disabled', true); // Get current settings var settings = { enable_coming_soon_mode: $('.seedprod-toggle[data-mode="coming_soon"]').is(':checked'), enable_maintenance_mode: $('.seedprod-toggle[data-mode="maintenance"]').is(':checked'), enable_login_mode: $('.seedprod-toggle[data-mode="login"]').is(':checked'), enable_404_mode: $('.seedprod-toggle[data-mode="404"]').is(':checked') }; // Apply the change if (mode === 'coming_soon') { settings.enable_coming_soon_mode = isChecked; // If enabling coming soon, disable maintenance if (isChecked && settings.enable_maintenance_mode) { settings.enable_maintenance_mode = false; $('.seedprod-toggle[data-mode="maintenance"]').prop('checked', false); updateModeCardStatus('maintenance', false); } } else if (mode === 'maintenance') { settings.enable_maintenance_mode = isChecked; // If enabling maintenance, disable coming soon if (isChecked && settings.enable_coming_soon_mode) { settings.enable_coming_soon_mode = false; $('.seedprod-toggle[data-mode="coming_soon"]').prop('checked', false); updateModeCardStatus('coming_soon', false); } } else if (mode === 'login') { settings.enable_login_mode = isChecked; } else if (mode === '404') { settings.enable_404_mode = isChecked; } // Update visual status immediately for better UX updateModeCardStatus(mode, isChecked); // Save settings via AJAX $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_save_settings', settings: JSON.stringify(settings), _wpnonce: seedprod_admin.nonce }, success: function() { // Refresh page for coming soon and maintenance modes to update admin bar notice if (mode === 'coming_soon' || mode === 'maintenance') { // Show success state briefly before refresh setTimeout(function() { window.location.reload(); }, 500); } else { // Re-enable toggle for other modes $toggle.prop('disabled', false); } }, error: function() { // Revert toggle on error $toggle.prop('checked', !isChecked); $toggle.prop('disabled', false); updateModeCardStatus(mode, !isChecked); alert(seedprod_admin.strings.settings_error || 'Could not save settings. Please try again.'); } }); }); /** * Helper function to update mode card status badge */ function updateModeCardStatus(mode, isActive) { var $card = $('.seedprod-mode-card[data-mode="' + mode + '"]'); var $label = $card.find('.seedprod-toggle-label'); if (isActive) { $label.html('<span class="active">' + (seedprod_admin.strings.active || 'ACTIVE') + '</span>'); } else { $label.html('<span class="inactive">' + (seedprod_admin.strings.inactive || 'INACTIVE') + '</span>'); } } // New landing page inline form functionality if ($('#seedprod-create-page-form').length > 0) { // Track if user has manually edited the slug var slugManuallyEdited = false; // Slugify function (same as Vue implementation) function slugify(text) { if (!text) return ''; return text.toString().toLowerCase() .replace(/\s+/g, '-') // Replace spaces with - .replace(/[^\w\-]+/g, '') // Remove all non-word chars .replace(/\-\-+/g, '-') // Replace multiple - with single - .replace(/^-+/, '') // Trim - from start of text .replace(/-+$/, ''); // Trim - from end of text } // Show/hide inline form $('#seedprod-show-new-page-form').on('click', function() { $('#seedprod-new-page-inline').slideDown(); $('#seedprod-page-name').focus(); $(this).prop('disabled', true); // Reset the manual edit flag when form is shown slugManuallyEdited = false; }); // Cancel button $('#seedprod-cancel-new-page').on('click', function() { $('#seedprod-new-page-inline').slideUp(); $('#seedprod-show-new-page-form').prop('disabled', false); // Reset form and flags $('#seedprod-create-page-form')[0].reset(); slugManuallyEdited = false; }); // Auto-generate slug from page name if user hasn't manually edited it $('#seedprod-page-name').on('input', function() { var pageName = $(this).val(); // Only auto-generate if user hasn't manually edited the slug if (!slugManuallyEdited) { var slug = slugify(pageName); $('#seedprod-page-slug').val(slug); } }); // When user types in slug field, mark it as manually edited $('#seedprod-page-slug').on('input', function() { slugManuallyEdited = true; }); // Clean up slug field on blur (when user leaves the field) $('#seedprod-page-slug').on('blur', function() { var originalVal = $(this).val(); var slug = slugify(originalVal); $(this).val(slug); }); // Handle form submission (use .off().on() to prevent duplicate handlers) $('#seedprod-create-page-form').off('submit').on('submit', function(e) { e.preventDefault(); var pageName = $('#seedprod-page-name').val(); var slug = $('#seedprod-page-slug').val(); // If no slug provided, generate from name if (!slug) { slug = slugify(pageName); } if (!pageName) { alert(seedprod_admin.strings.page_name_required || 'Please enter a page name'); $('#seedprod-page-name').focus(); return false; } // Hide any previous error messages $('#seedprod-page-slug-error').hide(); // Check if slug already exists $.ajax({ url: seedprod_slug_check_url, type: 'POST', data: { post_name: slug }, success: function(response) { if (response.success) { // Slug is available, proceed to template selection var templateUrl = seedprod_admin.admin_url + 'admin.php?page=seedprod_lite_template_selection' + '&type=lp' + '&_wpnonce=' + seedprod_admin.nonce + '&name=' + encodeURIComponent(pageName) + '&slug=' + encodeURIComponent(slug); window.location.href = templateUrl; } else { // Slug already exists - show error message below field $('#seedprod-page-slug-error').show(); $('#seedprod-page-slug').focus(); // Show alert after a brief delay so the error message is visible setTimeout(function() { alert(seedprod_admin.strings.slug_exists || 'This page URL already exists. Please choose a unique page URL.'); }, 100); } }, error: function(xhr, status, error) { // Only show error if it's not a validation error if (xhr.status !== 0) { alert(seedprod_admin.strings.network_error || 'Network error. Please try again.'); } } }); }); } // DataTable row actions initLandingPageTableActions(); // Search box clear functionality initLandingPageSearchClear(); } /** * Initialize search clear functionality for landing pages */ function initLandingPageSearchClear() { // Handle search input clear button (X button) // WordPress adds a clear button to search inputs in modern browsers var $searchInput = $('#seedprod-landing-pages-search-input'); if ($searchInput.length) { // Listen for the search event which fires when the X is clicked $searchInput.on('search', function() { // If the input is now empty (X was clicked) if ($(this).val() === '') { // Submit the form to clear the search $('#seedprod-landing-pages-form').submit(); } }); // Also handle the input event for older browsers $searchInput.on('input', function() { // If the input was cleared if ($(this).val() === '') { // Check if there was a search parameter in the URL var urlParams = new URLSearchParams(window.location.search); if (urlParams.has('s')) { // Clear the search by submitting the form $('#seedprod-landing-pages-form').submit(); } } }); } } /** * Initialize landing page table row actions (duplicate, trash, restore, delete) */ function initLandingPageTableActions() { // Helper function to show success message function showSuccessMessage(message) { var $message = $('<div class="notice notice-success is-dismissible" style="position: fixed; top: 32px; right: 20px; z-index: 9999; padding: 12px 20px;"><p>' + message + '</p></div>'); $('body').append($message); } // Duplicate page action $(document).on('click', '.seedprod-duplicate-page', function(e) { e.preventDefault(); var $link = $(this); var pageId = $link.data('id'); // Prevent duplicate clicks if ($link.hasClass('duplicating')) { return; } // Disable the link during AJAX $link.addClass('duplicating').css('pointer-events', 'none').css('opacity', '0.5'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_duplicate_lpage', id: pageId, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Show success message and reload showSuccessMessage(response.data.message); setTimeout(function() { window.location.reload(); }, 700); } else { alert(response.data || seedprod_admin.strings.duplicate_page_error); $link.removeClass('duplicating').css('pointer-events', '').css('opacity', ''); } }, error: function() { alert(seedprod_admin.strings.duplicate_page_error); $link.removeClass('duplicating').css('pointer-events', '').css('opacity', ''); } }); }); // Trash page action $(document).on('click', '.seedprod-trash-page', function(e) { e.preventDefault(); var $link = $(this); var pageId = $link.data('id'); // Prevent duplicate clicks if ($link.hasClass('trashing')) { return; } if (!confirm(seedprod_admin.strings.confirm_page_trash)) { return; } // Disable the link during AJAX $link.addClass('trashing').css('pointer-events', 'none').css('opacity', '0.5'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_trash_lpage', id: pageId, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Show success message and reload showSuccessMessage(response.data.message); setTimeout(function() { window.location.reload(); }, 700); } else { alert(response.data || seedprod_admin.strings.trash_page_error); $link.removeClass('trashing').css('pointer-events', '').css('opacity', ''); } }, error: function() { alert(seedprod_admin.strings.trash_page_error); $link.removeClass('trashing').css('pointer-events', '').css('opacity', ''); } }); }); // Restore page action $(document).on('click', '.seedprod-restore-page', function(e) { e.preventDefault(); var $link = $(this); var pageId = $link.data('id'); // Prevent duplicate clicks if ($link.hasClass('restoring')) { return; } // Disable the link during AJAX $link.addClass('restoring').css('pointer-events', 'none').css('opacity', '0.5'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_restore_lpage', id: pageId, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Show success message and reload showSuccessMessage(response.data.message); setTimeout(function() { window.location.reload(); }, 700); } else { alert(response.data || seedprod_admin.strings.restore_page_error); $link.removeClass('restoring').css('pointer-events', '').css('opacity', ''); } }, error: function() { alert(seedprod_admin.strings.restore_page_error); $link.removeClass('restoring').css('pointer-events', '').css('opacity', ''); } }); }); // Delete permanently action $(document).on('click', '.seedprod-delete-page', function(e) { e.preventDefault(); var $link = $(this); var pageId = $link.data('id'); // Prevent duplicate clicks if ($link.hasClass('deleting')) { return; } if (!confirm(seedprod_admin.strings.confirm_page_delete)) { return; } // Disable the link during AJAX $link.addClass('deleting').css('pointer-events', 'none').css('opacity', '0.5'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_delete_lpage', id: pageId, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Show success message and reload showSuccessMessage(response.data.message); setTimeout(function() { window.location.reload(); }, 700); } else { alert(response.data || seedprod_admin.strings.delete_page_error); $link.removeClass('deleting').css('pointer-events', '').css('opacity', ''); } }, error: function() { alert(seedprod_admin.strings.delete_page_error); $link.removeClass('deleting').css('pointer-events', '').css('opacity', ''); } }); }); // Handle bulk actions (unified handler) $('#doaction, #doaction2').off('click.bulk-actions').on('click.bulk-actions', function(e) { var action = $(this).prev('select').val(); if (action === '-1' || action === '') { return; } // Determine which page we're on var currentPage = new URLSearchParams(window.location.search).get('page'); if (currentPage === 'seedprod_lite_landing_pages') { // Handle landing pages bulk actions var checkedBoxes = $('#the-list input[name="page_id[]"]:checked'); if (checkedBoxes.length === 0) { alert(seedprod_admin.strings.no_pages_selected); e.preventDefault(); return; } // Collect page IDs var pageIds = []; checkedBoxes.each(function() { pageIds.push($(this).val()); }); // Confirm action var confirmMessage = ''; switch(action) { case 'trash': confirmMessage = seedprod_admin.strings.confirm_bulk_trash || 'Are you sure you want to trash the selected items?'; break; case 'restore': confirmMessage = seedprod_admin.strings.confirm_bulk_restore || 'Are you sure you want to restore the selected items?'; break; case 'delete': confirmMessage = seedprod_admin.strings.confirm_bulk_delete || 'Are you sure you want to permanently delete the selected items?'; break; default: return; } if (!confirm(confirmMessage)) { e.preventDefault(); return; } e.preventDefault(); // Prevent duplicate clicks var $button = $(this); if ($button.hasClass('bulk-processing')) { return; } // Disable the button during AJAX $button.addClass('bulk-processing').prop('disabled', true); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_bulk_action_lpages', bulk_action: action, page_ids: pageIds, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Show success message and reload showSuccessMessage(response.data.message); setTimeout(function() { window.location.reload(); }, 700); } else { alert(response.data || seedprod_admin.strings.bulk_action_error); $button.removeClass('bulk-processing').prop('disabled', false); } }, error: function() { alert(seedprod_admin.strings.bulk_action_error); $button.removeClass('bulk-processing').prop('disabled', false); } }); } else if (currentPage === 'seedprod_lite_website_builder') { // Handle templates bulk actions var templateIds = []; $('input[name="template_id[]"]:checked').each(function() { templateIds.push($(this).val()); }); if (templateIds.length === 0) { alert(seedprod_admin.strings.no_items_selected); e.preventDefault(); return; } var confirmMessage = ''; switch (action) { case 'trash': confirmMessage = seedprod_admin.strings.confirm_bulk_trash || 'Are you sure you want to trash the selected templates?'; break; case 'restore': confirmMessage = seedprod_admin.strings.confirm_bulk_restore || 'Are you sure you want to restore the selected templates?'; break; case 'delete': confirmMessage = seedprod_admin.strings.confirm_bulk_delete || 'Are you sure you want to permanently delete the selected templates?'; break; } if (!confirm(confirmMessage)) { e.preventDefault(); return; } e.preventDefault(); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_bulk_action_templates', bulk_action: action, template_ids: templateIds, nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { showNotice(response.data.message, 'success'); setTimeout(function() { location.reload(); }, 1000); } else { showNotice(response.data.message || seedprod_admin.strings.error, 'error'); } }, error: function() { showNotice(seedprod_admin.strings.error, 'error'); } }); } }); } /** * Initialize template selection functionality */ function initTemplateSelection() { var templateState = { currentTab: 'all-templates', search: '', filter: 'all', templates: {}, favorites: [], saved: [], templateHover: null, searchTimeout: null, resizeTimeout: null }; // Initialize based on current tab if (typeof seedprodTemplateData !== 'undefined') { templateState.currentTab = seedprodTemplateData.activeTab || 'all-templates'; } // Check for active filter pill set by PHP (for pre-filtering based on page type) var $activeFilter = $('.seedprod-filter-pill.active'); if ($activeFilter.length > 0) { templateState.filter = $activeFilter.data('filter') || 'all'; } // Load templates based on current tab (no need for nested document ready) if (templateState.currentTab === 'all-templates') { loadTemplates(); } else if (templateState.currentTab === 'favorite-templates') { loadFavoriteTemplates(); } else if (templateState.currentTab === 'saved-templates') { loadSavedTemplates(); } // Tab switching functionality $('.nav-tab').on('click', function() { var href = $(this).attr('href'); if (href && href.indexOf('tab=') !== -1) { var newTab = href.split('tab=')[1].split('&')[0]; templateState.currentTab = newTab; } }); // Search functionality with debouncing $('#seedprod-template-search').on('input', function() { var searchTerm = $(this).val().trim(); templateState.search = searchTerm; // Reset filter to 'all' when searching if (searchTerm !== '') { templateState.filter = 'all'; $('.seedprod-filter-pill').removeClass('active'); $('.seedprod-filter-pill[data-filter="all"]').addClass('active'); } // Clear existing timeout if (templateState.searchTimeout) { clearTimeout(templateState.searchTimeout); } // Set new timeout for 300ms delay templateState.searchTimeout = setTimeout(function() { if (templateState.currentTab === 'all-templates') { loadTemplates(); } }, 300); }); // Filter pill functionality $('.seedprod-filter-pill').on('click', function() { var filter = $(this).data('filter'); templateState.filter = filter; // Clear search when selecting a filter templateState.search = ''; $('#seedprod-template-search').val(''); // Update active states $('.seedprod-filter-pill').removeClass('active'); $(this).addClass('active'); loadTemplates(); }); // Template hover effects $(document).on('mouseenter', '.seedprod-template-card', function() { var templateId = $(this).data('template-id'); setTemplateHover(templateId, true); }); $(document).on('mouseleave', '.seedprod-template-card', function() { setTemplateHover(null, false); }); // Template selection $(document).on('click', '.seedprod-template-select', function(e) { e.preventDefault(); var templateId = $(this).data('template-id'); selectTemplate(templateId); }); // Template preview $(document).on('click', '.seedprod-template-preview', function(e) { e.preventDefault(); var templateId = $(this).data('template-id'); var templateName = $(this).data('template-name') || (seedprod_admin.strings.template_preview || 'Template Preview'); previewTemplate(templateId, templateName); }); // Blank template fallback button $(document).on('click', '.seedprod-blank-template-fallback', function(e) { e.preventDefault(); // Use blank template ID (99999 is SeedProd's convention for blank) selectTemplate('99999'); }); // Preview modal close $(document).on('click', '.seedprod-modal-close, .seedprod-modal-overlay', function() { closePreviewModal(); }); // Favorite toggle $(document).on('click', '.seedprod-favorite-toggle', function(e) { e.preventDefault(); var templateId = $(this).data('template-id'); toggleFavorite(templateId); }); /** * Load templates from API */ function loadTemplates() { var $grid = $('#all-templates-grid'); showLoadingSpinner($grid); // Map filter names to category IDs (matching Vue.js implementation) var categoryMap = { 'all': '', 'coming-soon': '1', 'maintenance': '2', '404': '3', 'sales': '4', 'webinar': '5', 'lead-squeeze': '6', 'thank-you': '7', 'login': '8' }; var category = categoryMap[templateState.filter] || ''; $.ajax({ url: seedprodTemplateData.ajaxUrl, type: 'POST', data: { action: 'seedprod_lite_v2_get_templates', filter: 'all', // Always use 'all' as filter type category: category, // Send category ID to backend search: templateState.search, is_lite_view: seedprodTemplateData.isLiteView || false, free_subscribed: seedprodTemplateData.freeTemplatesSubscribed || false, _wpnonce: seedprodTemplateData.nonce }, success: function(response) { hideLoadingSpinner($grid); if (response.success && response.data) { templateState.templates = response.data; renderTemplates(response.data, $grid); } else { showErrorFallback($grid, seedprod_admin.strings.template_load_error); } }, error: function() { hideLoadingSpinner($grid); showErrorFallback($grid, seedprod_admin.strings.template_network_error); } }); } /** * Load favorite templates */ function loadFavoriteTemplates() { var $grid = $('#favorite-templates-grid'); showLoadingSpinner($grid); $.ajax({ url: seedprodTemplateData.ajaxUrl, type: 'POST', data: { action: 'seedprod_lite_v2_get_favorite_templates', _wpnonce: seedprodTemplateData.nonce }, success: function(response) { hideLoadingSpinner($grid); if (response.success) { templateState.favorites = response.data; if (!response.data || response.data.length === 0) { showNoTemplatesMessage($grid, seedprod_admin.strings.template_no_favorites); } else { renderTemplates(response.data, $grid); } } else { showErrorFallback($grid, seedprod_admin.strings.template_load_error); } }, error: function() { hideLoadingSpinner($grid); showErrorFallback($grid, seedprod_admin.strings.network_error || 'Network error. Please try again.'); } }); } /** * Load saved templates */ function loadSavedTemplates() { var $grid = $('#saved-templates-grid'); showLoadingSpinner($grid); $.ajax({ url: seedprodTemplateData.ajaxUrl, type: 'POST', data: { action: 'seedprod_lite_v2_get_saved_templates', _wpnonce: seedprodTemplateData.nonce }, success: function(response) { hideLoadingSpinner($grid); if (response.success) { templateState.saved = response.data; if (response.data.length === 0) { showNoTemplatesMessage($grid, seedprod_admin.strings.template_no_saved); } else { renderTemplates(response.data, $grid); } } else { showErrorFallback($grid, seedprod_admin.strings.saved_template_load_error || 'Could not load saved templates.'); } }, error: function() { hideLoadingSpinner($grid); showErrorFallback($grid, seedprod_admin.strings.network_error || 'Network error. Please try again.'); } }); } /** * Render templates in grid */ function renderTemplates(templates, $container) { var html = ''; // Render templates if (templates && templates.length > 0) { templates.forEach(function(template) { html += renderTemplateCard(template); }); } // Clear existing templates and add new ones $container.find('.seedprod-template-card').remove(); $container.find('.seedprod-templates-loading, .seedprod-error-message, .seedprod-no-templates').remove(); // Add templates $container.append(html); // Initialize masonry layout after images load initializeMasonryLayout($container); } /** * Render individual template card */ function renderTemplateCard(template) { // Debug: Log template data to see available fields if (window.seedprodDebugTemplates) { console.log('Template data:', template); } var templateId = template.id || template.ID; var templateName = template.name || template.post_title || (seedprod_admin.strings.untitled_template || 'Untitled Template'); var thumbnailUrl = ''; var isFavorite = template.is_favorite || false; var heartClass = isFavorite ? 'seedprod-favorited' : ''; // Check if this is a Pro template for Lite users var isProTemplate = false; var cardClass = ''; var proBadge = ''; if (seedprodTemplateData.isLiteView) { // Check free_w_email FIRST since these are actually free templates if (template.free_w_email == 1 && !seedprodTemplateData.freeTemplatesSubscribed) { // Free template that requires email subscription cardClass = ' seedprod-email-template'; proBadge = '<span class="seedprod-template-free-badge">FREE</span>'; } else if (template.free == 0 && template.free_w_email != 1) { // Pro template (but not free with email) isProTemplate = true; cardClass = ' coming-soon-template'; proBadge = '<span class="seedprod-template-pro-badge">PRO</span>'; } } // Determine thumbnail URL if (templateState.currentTab === 'saved-templates') { thumbnailUrl = 'https://assets.seedprod.com/preview-saved.png'; } else { thumbnailUrl = 'https://assets.seedprod.com/full_length_thumbnails/preview-' + templateId + '.jpeg'; } // Determine action icon (heart for templates, trash for saved) var actionIcon = ''; if (templateState.currentTab === 'saved-templates') { // Show trash icon for saved templates actionIcon = `<span class="seedprod-delete-saved" data-template-id="${templateId}" style="cursor: pointer; color: #8c8f94;"> <span class="dashicons dashicons-trash"></span> </span>`; } else { // Show heart icon for regular/favorite templates actionIcon = `<span class="seedprod-favorite-toggle ${heartClass}" data-template-id="${templateId}"> <span class="dashicons dashicons-heart"></span> </span>`; } return ` <div class="seedprod-template-card${cardClass}" data-template-id="${templateId}"> <div class="seedprod-template-thumbnail"> ${proBadge} <img src="${thumbnailUrl}" alt="${templateName}" class="seedprod-template-image" loading="lazy" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';" /> <div class="seedprod-template-image-fallback" style="display: none; height: 200px; background: #f6f7f7; align-items: center; justify-content: center; color: #8c8f94; font-size: 14px;"> <span>' + (seedprod_admin.strings.preview_not_available || 'Preview Not Available') + '</span> </div> <div class="seedprod-template-overlay" style="display: none;"> <button class="seedprod-template-select" data-template-id="${templateId}"> <span class="dashicons dashicons-${isProTemplate ? 'lock' : 'yes'}"></span> </button> <button class="seedprod-template-preview" data-template-id="${templateId}" data-template-name="${templateName}"> <span class="dashicons dashicons-search"></span> </button> </div> </div> <div class="seedprod-template-name"> <span class="template-name">${templateName}</span> ${actionIcon} </div> </div> `; } /** * Show loading spinner */ function showLoadingSpinner($container) { $container.find('.seedprod-template-card').remove(); $container.find('.seedprod-error-message, .seedprod-no-templates').remove(); if (!$container.find('.seedprod-templates-loading').length) { var loadingHtml = ` <div class="seedprod-templates-loading"> <span class="spinner is-active"></span> <p>${seedprod_admin.strings.loading_templates || 'Loading templates...'}</p> </div> `; $container.append(loadingHtml); } $container.find('.seedprod-templates-loading').show(); } /** * Hide loading spinner */ function hideLoadingSpinner($container) { $container.find('.seedprod-templates-loading').remove(); } /** * Show error fallback with blank template option */ function showErrorFallback($container, message) { $container.find('.seedprod-template-card').remove(); $container.find('.seedprod-templates-loading').remove(); // Check if we have page name and slug to proceed with blank template var canCreateBlank = seedprodTemplateData && seedprodTemplateData.pageName && seedprodTemplateData.pageSlug; var errorHtml = ` <div class="seedprod-error-message"> <div class="seedprod-error-icon"> <span class="dashicons dashicons-warning"></span> </div> <h3>' + (seedprod_admin.strings.unable_load_templates || 'Unable to Load Templates') + '</h3> <p>${message}</p> ${canCreateBlank ? ` <div class="seedprod-error-actions"> <p>' + (seedprod_admin.strings.create_blank_template || 'You can still create a page with a blank template:') + '</p> <button class="button button-primary seedprod-blank-template-fallback"> <span class="dashicons dashicons-edit"></span> ' + (seedprod_admin.strings.start_blank_template || 'Start with Blank Template') + ' </button> <p class="seedprod-error-note">' + (seedprod_admin.strings.refresh_templates || 'or try refreshing the page to reload templates') + '</p> </div> ` : ` <div class="seedprod-error-actions"> <button class="button button-secondary" onclick="location.reload()"> <span class="dashicons dashicons-update"></span> ' + (seedprod_admin.strings.refresh_page || 'Refresh Page') + ' </button> </div> `} </div> `; $container.append(errorHtml); } /** * Show no templates message */ function showNoTemplatesMessage($container, message) { $container.find('.seedprod-template-card').remove(); $container.find('.seedprod-templates-loading').remove(); var noTemplatesHtml = ` <div class="seedprod-no-templates"> <p>${message}</p> </div> `; $container.append(noTemplatesHtml); } /** * Set template hover state */ function setTemplateHover(templateId, isHovering) { templateState.templateHover = isHovering ? templateId : null; // Show/hide overlays $('.seedprod-template-overlay').hide(); if (isHovering && templateId) { $('.seedprod-template-card[data-template-id="' + templateId + '"] .seedprod-template-overlay').show(); } } /** * Select template and redirect to builder */ function selectTemplate(templateId) { // Check if this is a Pro template in Lite view FIRST (before checking page data) if (seedprodTemplateData && seedprodTemplateData.isLiteView && templateId !== '99999') { // Find the template in our state to check if it's Pro var template = null; if (templateState.templates) { template = templateState.templates.find(function(t) { return t.id == templateId; }); } // Check if it requires email subscription FIRST (free_w_email == 1) // These templates have free: 0 but are actually free with email, so check them first if (template && template.free_w_email == 1 && !seedprodTemplateData.freeTemplatesSubscribed) { // Scroll to top to show subscription form window.scrollTo({ top: 0, behavior: 'smooth' }); // Add highlight effect to the subscription banner $('.seedprod-free-templates-banner').addClass('seedprod-highlight'); // Remove highlight after animation setTimeout(function() { $('.seedprod-free-templates-banner').removeClass('seedprod-highlight'); }, 3000); alert(seedprod_admin.strings.email_subscribe_required || 'Please subscribe with your email above to unlock this FREE template.'); return; } // Check if it's a Pro template (free == 0 but not free_w_email) if (template && template.free == 0 && template.free_w_email != 1) { // Redirect to upgrade page window.open(seedprod_admin.urls.upgrade_lite, '_blank'); return; } } // Only check for page data if we're actually going to create a page if (typeof seedprodTemplateData === 'undefined' || !seedprodTemplateData.pageName) { // If we're just testing, show a different message if (window.location.href.indexOf('test_lite=1') > -1) { alert(seedprod_admin.strings.test_mode_notice); } else { alert(seedprod_admin.strings.page_data_missing); } return; } // Show loading state var $button = $('.seedprod-template-select[data-template-id="' + templateId + '"]'); var $templateCard = $button.closest('.seedprod-template-card'); var $overlay = $button.closest('.seedprod-template-overlay'); // Keep overlay visible during processing $templateCard.addClass('coming-sooncessing'); $overlay.addClass('coming-sooncessing-active'); $button.prop('disabled', true); $button.find('.dashicons').removeClass('dashicons-yes').addClass('dashicons-update'); $button.find('.dashicons').addClass('seedprod-spin'); // Blank template should also go through page creation process // (Removed direct redirect - blank template needs to create a page too) // Prepare data for request var requestData = { action: 'seedprod_lite_v2_create_page_from_template', template_id: templateId, page_name: seedprodTemplateData.pageName, page_slug: seedprodTemplateData.pageSlug, page_type: seedprodTemplateData.pageType, _wpnonce: seedprodTemplateData.nonce }; // Include page_id if we're updating an existing page (edge case) if (seedprodTemplateData.pageId) { requestData.page_id = seedprodTemplateData.pageId; } // Create page from template $.ajax({ url: seedprodTemplateData.ajaxUrl, type: 'POST', data: requestData, success: function(response) { if (response.success && response.data.builder_url) { window.location.href = response.data.builder_url; } else { alert(response.data || seedprod_admin.strings.template_create_error || 'Could not create page from template. Please try again.'); resetSelectButton($button); } }, error: function() { alert(seedprod_admin.strings.network_error || 'Network error. Please try again.'); resetSelectButton($button); } }); } /** * Reset select button state */ function resetSelectButton($button) { var $templateCard = $button.closest('.seedprod-template-card'); var $overlay = $button.closest('.seedprod-template-overlay'); // Remove processing classes $templateCard.removeClass('coming-sooncessing'); $overlay.removeClass('coming-sooncessing-active'); $button.prop('disabled', false); $button.find('.dashicons').removeClass('dashicons-update seedprod-spin').addClass('dashicons-yes'); } /** * Preview template in modal */ function previewTemplate(templateId, templateName) { // Phase 1: Use static CDN previews from assets.seedprod.com (matching Vue.js implementation) var previewUrl = 'https://assets.seedprod.com/preview-' + templateId + '.html'; // Phase 2 TODO: Implement device switcher functionality // - Add device parameter to URL // - Handle responsive iframe sizing // - Update active device button state $('#seedprod-preview-title').text(templateName); $('#seedprod-preview-iframe').attr('src', previewUrl); $('#seedprod-template-preview-modal').show(); } /** * Close preview modal */ function closePreviewModal() { $('#seedprod-template-preview-modal').hide(); $('#seedprod-preview-iframe').attr('src', ''); } /** * Toggle favorite status */ function toggleFavorite(templateId) { var $favoriteButton = $('.seedprod-favorite-toggle[data-template-id="' + templateId + '"]'); var isFavorited = $favoriteButton.hasClass('seedprod-favorited'); var method = isFavorited ? 'detach' : 'attach'; // Optimistically update UI if (isFavorited) { $favoriteButton.removeClass('seedprod-favorited'); } else { $favoriteButton.addClass('seedprod-favorited'); } $.ajax({ url: seedprodTemplateData.ajaxUrl, type: 'POST', data: { action: 'seedprod_lite_v2_toggle_favorite_template', template_id: templateId, method: method, _wpnonce: seedprodTemplateData.nonce }, success: function(response) { if (response.success) { var isFavorite = response.data.is_favorite; // Update heart icon state to match server response if (isFavorite) { $favoriteButton.addClass('seedprod-favorited'); } else { $favoriteButton.removeClass('seedprod-favorited'); } // Update template state for (var i in templateState.templates) { if (templateState.templates[i].id == templateId) { templateState.templates[i].is_favorite = isFavorite; break; } } // If we're on favorites tab and unfavoriting, remove from display if (templateState.currentTab === 'favorite-templates' && !isFavorite) { $('.seedprod-template-card[data-template-id="' + templateId + '"]').fadeOut(function() { $(this).remove(); // Check if any favorites left if ($('#favorite-templates-grid .seedprod-template-card').length === 0) { showNoTemplatesMessage($('#favorite-templates-grid'), seedprod_admin.strings.no_favorites_found || 'No favorite templates found. Click the heart icon on any template to add it to your favorites.'); } }); } } else { // Revert optimistic update on failure if (method === 'attach') { $favoriteButton.removeClass('seedprod-favorited'); } else { $favoriteButton.addClass('seedprod-favorited'); } console.error(seedprod_admin.strings.favorite_toggle_error || 'Could not toggle favorite:', response.data); } }, error: function() { // Revert optimistic update on error if (method === 'attach') { $favoriteButton.removeClass('seedprod-favorited'); } else { $favoriteButton.addClass('seedprod-favorited'); } console.error(seedprod_admin.strings.favorite_network_error || 'Network error when toggling favorite'); } }); } /** * Initialize masonry layout */ function initializeMasonryLayout($container) { // Wait for images to load before calculating positions var $images = $container.find('.seedprod-template-image'); var imagesToLoad = $images.length; var imagesLoaded = 0; // If no images, layout immediately if (imagesToLoad === 0) { applyMasonryLayout($container); return; } // Wait for each image to load $images.each(function() { var $img = $(this); var img = new Image(); img.onload = img.onerror = function() { imagesLoaded++; if (imagesLoaded >= imagesToLoad) { // Small delay to ensure DOM is updated setTimeout(function() { applyMasonryLayout($container); }, 50); } }; // Trigger load (in case image is cached) if ($img.get(0).complete) { img.onload(); } else { img.src = $img.attr('src'); } }); } /** * Apply masonry layout to container */ function applyMasonryLayout($container) { var $cards = $container.find('.seedprod-template-card'); var containerWidth = $container.width(); var cardMargin = 24; // Calculate number of columns based on container width var columns; if (containerWidth > 1500) columns = 5; else if (containerWidth > 1000) columns = 4; else if (containerWidth > 700) columns = 3; else if (containerWidth > 400) columns = 2; else columns = 1; var cardWidth = Math.floor((containerWidth - (cardMargin * (columns - 1))) / columns); var columnHeights = new Array(columns).fill(0); // Position each card $cards.each(function() { var $card = $(this); // Set card width $card.css('width', cardWidth + 'px'); // Find shortest column var shortestColumn = 0; var shortestHeight = columnHeights[0]; for (var i = 1; i < columns; i++) { if (columnHeights[i] < shortestHeight) { shortestHeight = columnHeights[i]; shortestColumn = i; } } // Position card var left = shortestColumn * (cardWidth + cardMargin); var top = columnHeights[shortestColumn]; $card.css({ 'position': 'absolute', 'left': left + 'px', 'top': top + 'px' }); // Update column height columnHeights[shortestColumn] += $card.outerHeight() + cardMargin; }); // Set container height var maxHeight = Math.max.apply(Math, columnHeights); $container.css({ 'position': 'relative', 'height': maxHeight + 'px' }); } // Recalculate masonry on window resize $(window).on('resize.seedprodMasonry', function() { if ($('.seedprod-templates-grid').length) { clearTimeout(templateState.resizeTimeout); templateState.resizeTimeout = setTimeout(function() { $('.seedprod-templates-grid').each(function() { var $container = $(this); if ($container.find('.seedprod-template-card').length > 0) { applyMasonryLayout($container); } }); }, 250); } }); } /** * Website Builder Theme Toggle */ function initWebsiteBuilderThemeToggle() { var $toggle = $('#seedprod-theme-toggle'); if (!$toggle.length) return; var isProcessing = false; $toggle.on('change', function() { if (isProcessing) return; var $this = $(this); var enabled = $this.is(':checked'); var $label = $this.closest('.seedprod-theme-toggle-control').find('.seedprod-toggle-label'); var $stats = $('.seedprod-theme-stats'); // Show loading state isProcessing = true; $this.prop('disabled', true); $label.html('<span style="padding: 3px 8px; border-radius: 3px; background-color: #f0f0f0;"><span class="dashicons dashicons-update spin"></span> ' + seedprod_admin.strings.loading + '</span>'); // Make AJAX request $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_update_theme_enabled', nonce: seedprod_admin.v2_nonce, enabled: enabled }, success: function(response) { if (response.success) { // Update UI based on state if (response.data.enabled) { $label.html('<span class="active">' + seedprod_admin.strings.status_active + '</span>'); $stats.fadeIn(); // Check if we should prompt to create default pages checkDefaultPages(); } else { $label.html('<span class="inactive">' + seedprod_admin.strings.status_inactive + '</span>'); $stats.fadeOut(); } // Show success message showNotice(response.data.message, 'success'); // Scroll to top to show notice $('html, body').animate({ scrollTop: 0 }, 300); } else { // Revert toggle on error $this.prop('checked', !enabled); $label.html('<span class="' + (!enabled ? 'active' : 'inactive') + '">' + (!enabled ? seedprod_admin.strings.status_active : seedprod_admin.strings.status_inactive) + '</span>'); // Show error message showNotice(response.data || seedprod_admin.strings.settings_error, 'error'); // Scroll to top to show error $('html, body').animate({ scrollTop: 0 }, 300); } }, error: function() { // Revert toggle on error $this.prop('checked', !enabled); $label.html('<span class="' + (!enabled ? 'active' : 'inactive') + '">' + (!enabled ? seedprod_admin.strings.status_active : seedprod_admin.strings.status_inactive) + '</span>'); showNotice(seedprod_admin.strings.settings_error, 'error'); // Scroll to top to show error $('html, body').animate({ scrollTop: 0 }, 300); }, complete: function() { isProcessing = false; $this.prop('disabled', false); } }); }); } /** * Check if we should prompt to create default pages */ function checkDefaultPages() { // Check if WordPress is using static pages $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_check_default_pages', nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success && response.data.should_create) { // Show prompt to create default pages if (confirm(seedprod_admin.strings.create_default_pages_prompt)) { createDefaultPages(); } } }, error: function(xhr, status, error) { console.error('Error checking default pages:', error); } }); } /** * Create default home and blog pages */ function createDefaultPages() { $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_create_default_pages', nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { showNotice(response.data.message, 'success'); // Optionally refresh the page to show the new setup setTimeout(function() { window.location.reload(); }, 2000); } else { showNotice(response.message || 'Failed to create default pages', 'error'); } }, error: function(xhr, status, error) { console.error('Error creating default pages:', error); showNotice('An error occurred while creating default pages', 'error'); } }); } /** * Show admin notice */ function showNotice(message, type) { var noticeClass = type === 'success' ? 'notice-success' : 'notice-error'; var $notice = $('<div class="notice ' + noticeClass + ' is-dismissible seedprod-admin-notice"><p>' + message + '</p></div>'); // Insert at beginning of dashboard container for proper indentation $('.seedprod-dashboard-container').prepend($notice); // Auto-dismiss after 5 seconds setTimeout(function() { $notice.fadeOut(function() { $(this).remove(); }); }, 5000); // Make dismissible $notice.on('click', '.notice-dismiss', function() { $notice.fadeOut(function() { $(this).remove(); }); }); } /** * Theme Templates DataTable Functions */ function initThemeTemplatesTable() { // Toggle template publish status $(document).on('change', '.seedprod-template-toggle', function() { var $toggle = $(this); var templateId = $toggle.data('id'); var nonce = $toggle.data('nonce'); var $row = $toggle.closest('tr'); // Disable toggle during request $toggle.prop('disabled', true); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_toggle_template_status', template_id: templateId, nonce: nonce }, success: function(response) { if (response.success) { // Update row styling if needed if (response.data.status === 'publish') { $row.removeClass('status-draft').addClass('status-publish'); } else { $row.removeClass('status-publish').addClass('status-draft'); } showNotice(response.data.message, 'success'); } else { // Revert toggle $toggle.prop('checked', !$toggle.is(':checked')); showNotice(response.data || seedprod_admin.strings.error, 'error'); } }, error: function() { // Revert toggle $toggle.prop('checked', !$toggle.is(':checked')); showNotice(seedprod_admin.strings.error, 'error'); }, complete: function() { $toggle.prop('disabled', false); } }); }); // Duplicate template $(document).on('click', '.seedprod-duplicate-template', function(e) { e.preventDefault(); var $link = $(this); var templateId = $link.data('id'); var nonce = $link.data('nonce'); // Prevent duplicate clicks if ($link.hasClass('duplicating')) { return; } if (!confirm(seedprod_admin.strings.confirm_duplicate)) { return; } $link.addClass('duplicating disabled'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_duplicate_template', template_id: templateId, nonce: nonce }, success: function(response) { if (response.success) { showNotice(response.data.message, 'success'); // Always reload after a short delay for consistency setTimeout(function() { location.reload(); }, 700); } else { showNotice(response.data || seedprod_admin.strings.error, 'error'); $link.removeClass('duplicating disabled'); } }, error: function() { showNotice(seedprod_admin.strings.error, 'error'); $link.removeClass('duplicating disabled'); } }); }); // Trash template $(document).on('click', '.seedprod-trash-template', function(e) { e.preventDefault(); var $link = $(this); var templateId = $link.data('id'); var nonce = $link.data('nonce'); // Prevent duplicate clicks if ($link.hasClass('trashing')) { return; } if (!confirm(seedprod_admin.strings.confirm_trash)) { return; } $link.addClass('trashing disabled'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_trash_template', template_id: templateId, nonce: nonce }, success: function(response) { if (response.success) { showNotice(response.data.message, 'success'); // Always reload after a short delay for consistency setTimeout(function() { location.reload(); }, 700); } else { showNotice(response.data || seedprod_admin.strings.error, 'error'); $link.removeClass('trashing disabled'); } }, error: function() { showNotice(seedprod_admin.strings.error, 'error'); $link.removeClass('trashing disabled'); } }); }); // Restore template $(document).on('click', '.seedprod-restore-template', function(e) { e.preventDefault(); var $link = $(this); var templateId = $link.data('id'); var nonce = $link.data('nonce'); // Prevent duplicate clicks if ($link.hasClass('restoring')) { return; } $link.addClass('restoring disabled'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_restore_template', template_id: templateId, nonce: nonce }, success: function(response) { if (response.success) { showNotice(response.data.message, 'success'); // Always reload after a short delay for consistency setTimeout(function() { location.reload(); }, 700); } else { showNotice(response.data || seedprod_admin.strings.error, 'error'); $link.removeClass('restoring disabled'); } }, error: function() { showNotice(seedprod_admin.strings.error, 'error'); $link.removeClass('restoring disabled'); } }); }); // Delete template permanently $(document).on('click', '.seedprod-delete-template', function(e) { e.preventDefault(); var $link = $(this); var templateId = $link.data('id'); var nonce = $link.data('nonce'); // Prevent duplicate clicks if ($link.hasClass('deleting')) { return; } if (!confirm(seedprod_admin.strings.confirm_delete)) { return; } $link.addClass('deleting disabled'); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_delete_template', template_id: templateId, nonce: nonce }, success: function(response) { if (response.success) { showNotice(response.data.message, 'success'); // Always reload after a short delay for consistency setTimeout(function() { location.reload(); }, 700); } else { showNotice(response.data || seedprod_admin.strings.error, 'error'); $link.removeClass('deleting disabled'); } }, error: function() { showNotice(seedprod_admin.strings.error, 'error'); $link.removeClass('deleting disabled'); } }); }); // Handle bulk actions for theme templates $('#doaction, #doaction2').on('click', function(e) { console.log('Theme bulk action button clicked'); e.preventDefault(); // Stop any form submission first var action = $(this).prev('select').val(); console.log('Theme action selected:', action); if (action === '-1' || action === '') { return; } // Only handle on website builder page var currentPage = new URLSearchParams(window.location.search).get('page'); if (currentPage !== 'seedprod_lite_website_builder') { return; } // Get selected template IDs var templateIds = []; $('input[name="template_id[]"]:checked').each(function() { templateIds.push($(this).val()); }); console.log('Theme templates found:', templateIds.length, 'IDs:', templateIds); if (templateIds.length === 0) { alert(seedprod_admin.strings.no_items_selected || 'Please select at least one template.'); return; } // Confirm action var confirmMessage = ''; switch (action) { case 'trash': confirmMessage = seedprod_admin.strings.confirm_bulk_trash || 'Are you sure you want to trash the selected templates?'; break; case 'restore': confirmMessage = seedprod_admin.strings.confirm_bulk_restore || 'Are you sure you want to restore the selected templates?'; break; case 'delete': confirmMessage = seedprod_admin.strings.confirm_bulk_delete || 'Are you sure you want to permanently delete the selected templates?'; break; default: return; } if (!confirm(confirmMessage)) { return; } // Prevent duplicate clicks var $button = $(this); if ($button.hasClass('bulk-processing')) { return; } $button.addClass('bulk-processing').prop('disabled', true); $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_bulk_action_templates', bulk_action: action, template_ids: templateIds, nonce: seedprod_admin.v2_nonce }, success: function(response) { console.log('Theme bulk action response:', response); if (response.success) { showNotice(response.data.message, 'success'); setTimeout(function() { location.reload(); }, 1000); } else { showNotice(response.data.message || seedprod_admin.strings.error, 'error'); $button.removeClass('bulk-processing').prop('disabled', false); } }, error: function(xhr, status, error) { console.log('Theme bulk action error:', xhr, status, error); showNotice(seedprod_admin.strings.error, 'error'); $button.removeClass('bulk-processing').prop('disabled', false); } }); }); } /** * Initialize Add New Template Modal */ function initAddNewTemplateModal() { var $modal = $('#seedprod-new-template-modal'); if (!$modal.length) return; var $form = $('#seedprod-new-template-form'); var $overlay = $modal.find('.seedprod-modal-overlay'); var $closeBtn = $modal.find('.seedprod-modal-close'); var $cancelBtn = $modal.find('.seedprod-modal-cancel'); var $createBtn = $('#seedprod-create-template-btn'); var $templateType = $('#template-type'); var $templatePriority = $('#template-priority'); var $conditionsSection = $('#template-conditions-section'); var $conditionsList = $('#template-conditions-list'); var $addConditionBtn = $('.seedprod-add-template-condition'); var templateConditions = []; // Use PHP-provided conditions from localized script // This ensures conditions match the server-side logic and include WooCommerce + dynamic post types var conditionsMap = seedprod_admin.conditions || { 'General': [ { value: '_entire_site', text: 'Entire Site' }, { value: 'is_front_page', text: 'Front Page' }, { value: 'is_home', text: 'Blog Page' }, { value: 'is_page(x)', text: 'Pages' }, { value: 'is_singular', text: 'Pages and Posts' }, { value: 'is_404', text: 'Is 404' }, { value: 'is_author(x)', text: 'Is Author' } ], 'Posts': [ { value: 'is_single(x)', text: 'Posts' }, { value: 'has_category(x)', text: 'Has Category' }, { value: 'has_tag(x)', text: 'Has Tag' } ], 'Archives': [ { value: 'is_archive', text: 'All Archives' }, { value: 'is_date', text: 'Date Archives' }, { value: 'is_search', text: 'Search Results' }, { value: 'is_category(x)', text: 'Post Category Archives' }, { value: 'is_tag(x)', text: 'Post Tag Archives' } ] }; // Open modal when Add New Template button is clicked $(document).on('click', '.seedprod-add-new-template-btn', function(e) { e.preventDefault(); openModal(); }); // Close modal handlers $overlay.on('click', closeModal); $closeBtn.on('click', closeModal); $cancelBtn.on('click', closeModal); // Handle ESC key $(document).on('keydown', function(e) { if (e.key === 'Escape' && $modal.is(':visible')) { closeModal(); } }); // Handle template type change $templateType.on('change', function() { var type = $(this).val(); if (!type) { // Hide conditions if no type selected $conditionsSection.hide(); $conditionsList.empty(); templateConditions = []; return; } // Template parts don't need conditions if (type === 'part') { $conditionsSection.hide(); $conditionsList.empty(); templateConditions = []; $templatePriority.val(20); return; } // Show conditions section for all other types $conditionsSection.show(); // Clear existing conditions $conditionsList.empty(); templateConditions = []; // Set default priority and conditions based on type switch(type) { case 'header': case 'footer': // Headers and footers default to entire site $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: '_entire_site', value: '' }); break; case 'single_page': // Single page defaults to all pages $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_page(x)', value: '' }); break; case 'single_post': // Single post defaults to all posts $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_single(x)', value: '' }); break; case 'archive': // Archive defaults to all archives $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_archive', value: '' }); break; case 'search': // Search results page $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_search', value: '' }); break; case 'author': // Author page $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_author(x)', value: '' }); break; case 'custom': // Custom - let user define everything $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: '', value: '' }); break; case 'single_product': // WooCommerce single product $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_singular(product)', value: '' }); break; case 'archive_product': // WooCommerce product archive $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: 'is_post_type_archive(product)', value: '' }); break; default: // Default: entire site with lower priority $templatePriority.val(20); addTemplateConditionRow({ condition: 'include', type: '_entire_site', value: '' }); break; } }); // Add new condition button $addConditionBtn.on('click', function() { addTemplateConditionRow(); }); // Remove condition $(document).on('click', '#template-conditions-list .seedprod-remove-condition', function() { var $row = $(this).closest('.seedprod-condition-row'); var index = $row.index(); // Remove from array templateConditions.splice(index, 1); // Remove from DOM $row.remove(); // Update UI updateTemplateConditionsUI(); }); // Handle condition type change $(document).on('change', '#template-conditions-list .seedprod-condition-type', function() { var $row = $(this).closest('.seedprod-condition-row'); var selectedValue = $(this).val(); var $valueInput = $row.find('.seedprod-condition-value'); var index = $row.index(); // Update conditions array if (templateConditions[index]) { templateConditions[index].type = selectedValue; } // Show/hide value input based on condition type if (selectedValue.includes('(x)')) { $valueInput.show().attr('placeholder', 'Enter value (e.g., page ID, category name)'); } else { $valueInput.hide().val(''); if (templateConditions[index]) { templateConditions[index].value = ''; } } }); // Handle condition value change $(document).on('change', '#template-conditions-list .seedprod-condition-value', function() { var $row = $(this).closest('.seedprod-condition-row'); var index = $row.index(); if (templateConditions[index]) { templateConditions[index].value = $(this).val(); } }); // Handle include/exclude toggle $(document).on('change', '#template-conditions-list .seedprod-condition-mode', function() { var $row = $(this).closest('.seedprod-condition-row'); var index = $row.index(); $row.toggleClass('is-excluded', $(this).val() === 'exclude'); if (templateConditions[index]) { templateConditions[index].condition = $(this).val(); } }); // Handle form submission $createBtn.on('click', function(e) { e.preventDefault(); // Validate form var templateName = $('#template-name').val().trim(); var templateType = $('#template-type').val(); var templatePriority = $('#template-priority').val() || 10; if (!templateName) { showFieldError('#template-name', seedprod_admin.strings.template_name_required || 'Template name is required'); return; } if (!templateType) { showFieldError('#template-type', seedprod_admin.strings.template_type_required || 'Please select a template type'); return; } // Clear any previous errors clearFieldErrors(); // Show loading state var $buttonText = $createBtn.find('.button-text'); var $spinner = $createBtn.find('.spinner'); $createBtn.prop('disabled', true); $buttonText.hide(); $spinner.show(); // Prepare conditions for submission var finalConditions = []; templateConditions.forEach(function(cond) { if (cond.type) { var condition = { type: cond.type, condition: cond.condition || 'include', value: cond.value || '' }; // Keep type as-is with (x) placeholder, don't embed value // Value stays separate in the value field finalConditions.push(condition); } }); // Make AJAX request $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_create_template', template_name: templateName, template_type: templateType, template_priority: templatePriority, template_conditions: JSON.stringify(finalConditions), nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Close modal closeModal(); // Show success message showNotice('success', response.data.message || 'Template created successfully'); // Redirect to builder if (response.data.redirect_url) { window.location.href = response.data.redirect_url; } else if (response.data.template_id) { // Fallback: construct builder URL var builderUrl = seedprod_admin.admin_url + 'admin.php?page=seedprod_lite_builder&id=' + response.data.template_id + '#/'; window.location.href = builderUrl; } } else { // Show error showModalError(response.data || 'Failed to create template'); } }, error: function() { showModalError(seedprod_admin.strings.template_error || 'An error occurred. Please try again.'); }, complete: function() { // Reset button state $createBtn.prop('disabled', false); $buttonText.show(); $spinner.hide(); } }); }); // Helper: Open modal function openModal() { // Reset form $form[0].reset(); clearFieldErrors(); clearModalError(); // Reset conditions $conditionsSection.hide(); $conditionsList.empty(); templateConditions = []; // Reset priority $templatePriority.val(20); // Show modal $modal.fadeIn(200); $('body').addClass('seedprod-modal-open'); // Focus first input setTimeout(function() { $('#template-name').focus(); }, 250); } // Helper: Close modal function closeModal() { $modal.fadeOut(200); $('body').removeClass('seedprod-modal-open'); } // Helper: Add template condition row function addTemplateConditionRow(condition) { condition = condition || { type: '', value: '', condition: 'include' }; // Add to array templateConditions.push(condition); var rowHtml = '<div class="seedprod-condition-row' + (condition.condition === 'exclude' ? ' is-excluded' : '') + '">'; rowHtml += '<select class="seedprod-condition-mode">'; rowHtml += '<option value="include"' + (condition.condition === 'include' ? ' selected' : '') + '>Include</option>'; rowHtml += '<option value="exclude"' + (condition.condition === 'exclude' ? ' selected' : '') + '>Exclude</option>'; rowHtml += '</select>'; rowHtml += '<select class="seedprod-condition-type">'; rowHtml += '<option value="">' + (seedprod_admin.strings.condition_select_type || 'Select Type') + '</option>'; for (var group in conditionsMap) { rowHtml += '<optgroup label="' + group + '">'; conditionsMap[group].forEach(function(opt) { var selected = (opt.value === condition.type) ? ' selected' : ''; rowHtml += '<option value="' + opt.value + '"' + selected + '>' + opt.text + '</option>'; }); rowHtml += '</optgroup>'; } rowHtml += '</select>'; var showValue = condition.type && condition.type.includes('(x)'); var placeholderText = seedprod_admin.strings.condition_enter_value || 'Enter ids or slugs'; rowHtml += '<input type="text" class="seedprod-condition-value" placeholder="' + placeholderText + '" value="' + (condition.value || '') + '"'; if (!showValue) { rowHtml += ' style="display: none;"'; } rowHtml += '>'; rowHtml += '<button type="button" class="seedprod-remove-condition">'; rowHtml += '<span class="dashicons dashicons-trash"></span>'; rowHtml += '</button>'; rowHtml += '</div>'; $conditionsList.append(rowHtml); updateTemplateConditionsUI(); } // Helper: Update conditions UI function updateTemplateConditionsUI() { // Show remove button only if more than one condition var $rows = $('#template-conditions-list .seedprod-condition-row'); if ($rows.length <= 1) { $rows.find('.seedprod-remove-condition').hide(); } else { $rows.find('.seedprod-remove-condition').show(); } } // Helper: Show field error function showFieldError(fieldSelector, message) { var $field = $(fieldSelector); var $group = $field.closest('.seedprod-form-group'); // Remove any existing error $group.find('.seedprod-field-error').remove(); // Add error class and message $field.addClass('error'); $('<span class="seedprod-field-error">' + message + '</span>').insertAfter($field); } // Helper: Clear field errors function clearFieldErrors() { $form.find('.error').removeClass('error'); $form.find('.seedprod-field-error').remove(); } // Helper: Show modal error function showModalError(message) { clearModalError(); var $error = $('<div class="seedprod-modal-error"><span class="dashicons dashicons-warning"></span> ' + message + '</div>'); $form.prepend($error); } // Helper: Clear modal error function clearModalError() { $form.find('.seedprod-modal-error').remove(); } // Helper: Show notice function showNotice(type, message) { var noticeClass = type === 'success' ? 'notice-success' : 'notice-error'; var $notice = $('<div class="notice ' + noticeClass + ' is-dismissible"><p>' + message + '</p></div>'); // Insert notice after page title $('.seedprod-dashboard-page .seedprod-header').after($notice); // Auto dismiss after 5 seconds setTimeout(function() { $notice.fadeOut(function() { $(this).remove(); }); }, 5000); // Make dismissible $notice.on('click', '.notice-dismiss', function() { $notice.fadeOut(function() { $(this).remove(); }); }); } } /** * Initialize Edit Conditions Modal */ function initEditConditionsModal() { var $modal = $('#seedprod-conditions-modal'); if (!$modal.length) return; var $overlay = $modal.find('.seedprod-modal-overlay'); var $closeBtn = $modal.find('.seedprod-modal-close'); var $cancelBtn = $modal.find('.seedprod-modal-cancel'); var $saveBtn = $('#seedprod-save-conditions-btn'); var $conditionsList = $('#seedprod-conditions-list'); var $addConditionBtn = $('.seedprod-add-condition'); var $templateName = $('#seedprod-template-name'); var $templateType = $('#seedprod-template-type-display'); var $templatePriority = $('#seedprod-template-priority'); var currentTemplateId = null; // Conditions options - use PHP-provided conditions from localized script // This ensures conditions match the server-side logic and include dynamic post types var conditionsMap = seedprod_admin.conditions || {}; // Condition modes from PHP (Include, Exclude, Custom) var conditionModes = seedprod_admin.condition_modes || { 'include': 'Include', 'exclude': 'Exclude', 'custom': 'Custom' }; var typeLabels = ( window.seedprod_admin && seedprod_admin.template_type_labels ) || {}; // Open modal when Edit Conditions is clicked $(document).on('click', '.seedprod-edit-conditions', function(e) { e.preventDefault(); currentTemplateId = $(this).data('id'); openModal(); }); // Close modal handlers $overlay.on('click', closeModal); $closeBtn.on('click', closeModal); $cancelBtn.on('click', closeModal); // Handle ESC key $(document).on('keydown', function(e) { if (e.key === 'Escape' && $modal.is(':visible')) { closeModal(); } }); // Add new condition $addConditionBtn.on('click', function() { addConditionRow(); }); // Remove condition $(document).on('click', '.seedprod-remove-condition', function() { $(this).closest('.seedprod-condition-row').remove(); updateConditionsUI(); }); // Handle condition type change $(document).on('change', '.seedprod-condition-type', function() { var $row = $(this).closest('.seedprod-condition-row'); var selectedValue = $(this).val(); var $valueInput = $row.find('.seedprod-condition-value'); // Show/hide value input based on condition type if (selectedValue.includes('(x)')) { $valueInput.show().attr('placeholder', seedprod_admin.strings.condition_enter_value || 'Enter ids or slugs'); } else { $valueInput.hide().val(''); } }); // Handle include/exclude/custom toggle $(document).on('change', '.seedprod-condition-mode', function() { var $row = $(this).closest('.seedprod-condition-row'); var selectedMode = $(this).val(); var $typeSelect = $row.find('.seedprod-condition-type'); var $valueInput = $row.find('.seedprod-condition-value'); // Update row classes $row.toggleClass('is-excluded', selectedMode === 'exclude'); $row.toggleClass('is-custom', selectedMode === 'custom'); // Handle custom mode var placeholderText = seedprod_admin.strings.condition_enter_value || 'Enter ids or slugs'; if (selectedMode === 'custom') { // Hide type selector, show value input $typeSelect.hide(); $valueInput.show().attr('placeholder', placeholderText); } else { // Show type selector $typeSelect.show(); // Value input visibility depends on selected type var typeValue = $typeSelect.val(); if (typeValue && typeValue.includes('(x)')) { $valueInput.show().attr('placeholder', placeholderText); } else { $valueInput.hide().val(''); } } }); // Save conditions $saveBtn.on('click', function() { saveConditions(); }); // Helper: Decode HTML entities function decodeHtmlEntities(text) { var textArea = document.createElement('textarea'); textArea.innerHTML = text; return textArea.value; } // Helper: Open modal function openModal() { // Clear conditions list $conditionsList.empty(); // Show loading $conditionsList.html('<div class="spinner is-active" style="float: none; margin: 20px auto;"></div>'); // Fetch current template data $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_get_template_conditions', template_id: currentTemplateId, nonce: seedprod_admin.v2_nonce }, success: function(response) { $conditionsList.empty(); if (response.success) { // Load template name (decode HTML entities) if (response.data.name) { $templateName.val(decodeHtmlEntities(response.data.name)); } // Load template type var currentTemplateType = response.data.type; if (currentTemplateType) { var typeLabel = typeLabels[currentTemplateType] || currentTemplateType; $templateType.text(typeLabel); } // Load template priority if (response.data.priority !== undefined) { $templatePriority.val(response.data.priority); } else { $templatePriority.val(20); } // Cache selectors for performance var $conditionsFormGroup = $('.seedprod-form-group').has('#seedprod-conditions-list'); var $addConditionBtn = $('.seedprod-add-condition'); // Hide conditions section for Template Parts (type: 'part') if (currentTemplateType === 'part') { $conditionsFormGroup.hide(); $addConditionBtn.hide(); } else { // Show conditions section for other types $conditionsFormGroup.show(); $addConditionBtn.show(); // Load existing conditions var conditions = response.data.conditions; if (conditions && conditions.length > 0) { conditions.forEach(function(condition) { addConditionRow(condition); }); } else { // Add default empty row addConditionRow(); } } } else { // Add default empty row addConditionRow(); } }, error: function() { $conditionsList.empty(); addConditionRow(); } }); // Show modal $modal.fadeIn(200); $('body').addClass('seedprod-modal-open'); } // Helper: Close modal function closeModal() { $modal.fadeOut(200); $('body').removeClass('seedprod-modal-open'); } // Helper: Parse condition to separate type and value function parseCondition(condition) { condition = condition || { type: '', value: '', condition: 'include' }; // If the type contains a value in parentheses (e.g., "is_page(about)"), // extract the value and convert type to placeholder format var type = condition.type; var value = condition.value || ''; if (type && type.includes('(') && type.includes(')') && !type.includes('(x)')) { // Extract value from type like "is_page(about)" -> value: "about", type: "is_page(x)" var match = type.match(/^([^\(]+)\(([^\)]+)\)$/); if (match) { type = match[1] + '(x)'; value = match[2]; } } return { type: type, value: value, condition: condition.condition || 'include' }; } // Helper: Add condition row function addConditionRow(conditionData) { // Parse the condition to handle both formats var condition = parseCondition(conditionData); var isCustom = (condition.condition === 'custom'); var rowHtml = '<div class="seedprod-condition-row' + (condition.condition === 'exclude' ? ' is-excluded' : '') + (isCustom ? ' is-custom' : '') + '">'; rowHtml += '<select class="seedprod-condition-mode">'; // Build mode options from PHP data for (var modeKey in conditionModes) { var selected = (condition.condition === modeKey) ? ' selected' : ''; rowHtml += '<option value="' + modeKey + '"' + selected + '>' + conditionModes[modeKey] + '</option>'; } rowHtml += '</select>'; // Type selector (hidden for custom mode) rowHtml += '<select class="seedprod-condition-type"'; if (isCustom) { rowHtml += ' style="display: none;"'; } rowHtml += '>'; rowHtml += '<option value="">' + (seedprod_admin.strings.condition_select_type || 'Select Type') + '</option>'; for (var group in conditionsMap) { rowHtml += '<optgroup label="' + group + '">'; conditionsMap[group].forEach(function(opt) { var selected = (opt.value === condition.type) ? ' selected' : ''; rowHtml += '<option value="' + opt.value + '"' + selected + '>' + opt.text + '</option>'; }); rowHtml += '</optgroup>'; } rowHtml += '</select>'; // Value input (shown for custom mode or when type has (x)) var showValue = isCustom || (condition.type && condition.type.includes('(x)')); var placeholderText = seedprod_admin.strings.condition_enter_value || 'Enter ids or slugs'; rowHtml += '<input type="text" class="seedprod-condition-value" placeholder="' + placeholderText + '" value="' + (condition.value || '') + '"'; if (!showValue) { rowHtml += ' style="display: none;"'; } rowHtml += '>'; rowHtml += '<button type="button" class="seedprod-remove-condition">'; rowHtml += '<span class="dashicons dashicons-trash"></span>'; rowHtml += '</button>'; rowHtml += '</div>'; $conditionsList.append(rowHtml); updateConditionsUI(); } // Helper: Update UI function updateConditionsUI() { // Show remove button only if more than one condition var $rows = $('.seedprod-condition-row'); if ($rows.length <= 1) { $rows.find('.seedprod-remove-condition').hide(); } else { $rows.find('.seedprod-remove-condition').show(); } } // Helper: Save conditions function saveConditions() { // Get template name and priority var templateName = $templateName.val().trim(); var templatePriority = parseInt($templatePriority.val(), 10) || 20; // Validate template name if (!templateName) { alert(seedprod_admin.strings.template_name_required); $templateName.focus(); return; } // Collect conditions data var conditions = []; $('.seedprod-condition-row').each(function() { var $row = $(this); var conditionMode = $row.find('.seedprod-condition-mode').val(); var type = $row.find('.seedprod-condition-type').val(); var value = $row.find('.seedprod-condition-value').val() || ''; // For custom mode or if type is selected if (conditionMode === 'custom' || type) { var condition = { type: type || '', condition: conditionMode, value: value }; conditions.push(condition); } }); // Show loading var $buttonText = $saveBtn.find('.button-text'); var $spinner = $saveBtn.find('.spinner'); $saveBtn.prop('disabled', true); $buttonText.hide(); $spinner.show(); // Save via AJAX $.ajax({ url: seedprod_admin.ajax_url, type: 'POST', data: { action: 'seedprod_lite_v2_save_template_conditions', template_id: currentTemplateId, template_name: templateName, priority: templatePriority, conditions: JSON.stringify(conditions), nonce: seedprod_admin.v2_nonce }, success: function(response) { if (response.success) { // Close modal closeModal(); // Refresh the table row to show new conditions location.reload(); } else { alert(response.data || seedprod_admin.strings.save_conditions_error); } }, error: function() { alert(seedprod_admin.strings.error_occurred); }, complete: function() { // Reset button $saveBtn.prop('disabled', false); $buttonText.show(); $spinner.hide(); } }); } } // Download a file from base64-encoded data via Blob URL. // Works in all environments including WordPress Playground where // the Service Worker intercepts static file requests. function seedprod_lite_v2_download_base64_file(base64Data, filename, mimeType) { mimeType = mimeType || 'application/zip'; var byteCharacters = atob(base64Data); var byteArray = new Uint8Array(byteCharacters.length); for (var i = 0; i < byteCharacters.length; i++) { byteArray[i] = byteCharacters.charCodeAt(i); } var blob = new Blob([byteArray], { type: mimeType }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } /** * Initialize Import/Export Modal */ function initImportExportModal() { var $modal = $('#seedprod-import-export-modal'); if (!$modal.length) return; // Open modal when button is clicked $('#seedprod-import-export-btn').on('click', function(e) { e.preventDefault(); $modal.fadeIn(200); }); // Close modal $modal.find('.seedprod-modal-close, .seedprod-modal-cancel').on('click', function() { $modal.fadeOut(200); resetImportForm(); }); // Close modal when clicking on overlay $modal.on('click', function(e) { if (e.target === this) { $modal.fadeOut(200); resetImportForm(); } }); // Tab switching $modal.find('.seedprod-tab-button').on('click', function() { var $this = $(this); var tab = $this.data('tab'); // Update active tab button $modal.find('.seedprod-tab-button').removeClass('active'); $this.addClass('active'); // Show corresponding tab content $modal.find('.seedprod-tab-content').hide(); $modal.find('#seedprod-' + tab + '-tab').show(); }); // Export functionality $('#seedprod-export-theme-btn').on('click', function() { var $btn = $(this); var $spinner = $btn.find('.spinner'); var $text = $btn.find('.button-text'); var $status = $('.seedprod-export-status'); var originalText = $text.text(); // Show loading state $btn.prop('disabled', true); $spinner.show(); $text.text(seedprod_admin.strings.loading || 'Processing...'); $status.show().find('.notice').removeClass('notice-error notice-success').addClass('notice-info').find('p').text('Preparing export file...'); // Make AJAX request (matches landing page export pattern) $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'seedprod_lite_v2_export_theme_files', nonce: seedprod_admin.v2_nonce }, success: function(response) { $btn.prop('disabled', false); $spinner.hide(); $text.text(originalText); if (response.success && response.data && response.data.filedata) { // Build success message var successMsg = seedprod_admin.strings.export_completed || 'Export completed successfully! Downloading...'; if (response.data.warning) { successMsg = response.data.warning + ' Download starting...'; } $status.show().find('.notice').removeClass('notice-info notice-error').addClass('notice-success').find('p').text(successMsg); // Trigger download from inline base64 data via Blob URL. seedprod_lite_v2_download_base64_file(response.data.filedata, response.data.filename || 'seedprod-theme-export.zip'); // Hide success message after 3 seconds setTimeout(function() { $status.fadeOut(); }, 3000); } else { // Show error message var errorMsg = (response.data && response.data.message) ? response.data.message : (seedprod_admin.strings.export_failed || 'Export failed. Please try again.'); $status.show().find('.notice').removeClass('notice-info notice-success').addClass('notice-error').find('p').text(errorMsg); } }, error: function(xhr, status, error) { $btn.prop('disabled', false); $spinner.hide(); $text.text(originalText); var errorMsg = seedprod_admin.strings.export_failed || 'Export failed. Please try again.'; if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { errorMsg = xhr.responseJSON.data.message; } $status.show().find('.notice').removeClass('notice-info notice-success').addClass('notice-error').find('p').text(errorMsg); } }); }); // File selection $('#seedprod-select-file-btn').on('click', function() { $('#seedprod-import-file').click(); }); // Handle file selection $('#seedprod-import-file').on('change', function() { var file = this.files[0]; if (file) { $('.seedprod-file-name').text(file.name); $('#seedprod-import-theme-btn').prop('disabled', false); $('#seedprod-import-url').val(''); // Clear URL field } }); // Handle URL input $('#seedprod-import-url').on('input', function() { var url = $(this).val().trim(); if (url) { $('#seedprod-import-theme-btn').prop('disabled', false); // Clear file selection $('#seedprod-import-file').val(''); $('.seedprod-file-name').text(''); } else if (!$('#seedprod-import-file').val()) { $('#seedprod-import-theme-btn').prop('disabled', true); } }); // Helper function to perform the import function performThemeImport(file, url, deleteExisting) { var $btn = $('#seedprod-import-theme-btn'); var $spinner = $btn.find('.spinner'); var $text = $btn.find('.button-text'); var $status = $('.seedprod-import-status'); // Show loading state $btn.prop('disabled', true); $spinner.show(); $text.text(seedprod_admin.strings.loading || 'Importing...'); $status.show().find('.notice').removeClass('notice-error notice-success notice-warning').addClass('notice-info').find('p').text('Importing templates...'); if (file) { // File upload var formData = new FormData(); formData.append('action', 'seedprod_lite_v2_import_theme_files'); formData.append('nonce', seedprod_admin.v2_nonce); formData.append('seedprod_theme_files', file); formData.append('delete_existing', deleteExisting ? '1' : '0'); $.ajax({ url: ajaxurl, type: 'POST', data: formData, processData: false, contentType: false, success: function(response) { $btn.prop('disabled', false); $spinner.hide(); $text.text('Import Templates'); // The import function returns just true/false, not {success: true} if (response === true || response.success) { $status.find('.notice').removeClass('notice-info').addClass('notice-success').find('p').text('Templates imported successfully!'); var warnings = (response && response.data && response.data.warnings) ? response.data.warnings : []; seedprodRenderImportWarnings(warnings, $status); // Hold the page longer when there are warnings so users can read the list. var reloadDelay = warnings.length ? 8000 : 2000; setTimeout(function() { window.location.reload(); }, reloadDelay); } else if (response === false) { $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(seedprod_admin.strings.error_occurred || 'An error occurred. Please try again.'); } else { var errorMessage = response.data || 'Import failed. Please try again.'; if (typeof response.data === 'object' && response.data.message) { errorMessage = response.data.message; } $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(errorMessage); } }, error: function(xhr, status, error) { $btn.prop('disabled', false); $spinner.hide(); $text.text('Import Templates'); var errorMessage = 'Import failed: ' + (error || status || 'Unknown error'); if (xhr.responseJSON && xhr.responseJSON.data) { errorMessage = xhr.responseJSON.data; } $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(errorMessage); } }); } else { // URL import $.post(ajaxurl, { action: 'seedprod_lite_v2_import_theme_by_url', nonce: seedprod_admin.v2_nonce, seedprod_theme_url: url, delete_existing: deleteExisting ? '1' : '0' }, function(response) { $btn.prop('disabled', false); $spinner.hide(); $text.text('Import Templates'); // The import function returns just true/false, not {success: true} if (response === true || response.success) { $status.find('.notice').removeClass('notice-info').addClass('notice-success').find('p').text('Templates imported successfully!'); var warnings = (response && response.data && response.data.warnings) ? response.data.warnings : []; seedprodRenderImportWarnings(warnings, $status); var reloadDelay = warnings.length ? 8000 : 2000; setTimeout(function() { window.location.reload(); }, reloadDelay); } else if (response === false) { $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text('Import failed. Please check the URL and try again.'); } else { $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(response.data || 'Import failed. Please try again.'); } }).fail(function() { $btn.prop('disabled', false); $spinner.hide(); $text.text('Import Templates'); $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text('Import failed. Please try again.'); }); } } // Import functionality with confirmation $('#seedprod-import-theme-btn').on('click', function() { var file = $('#seedprod-import-file')[0].files[0]; var url = $('#seedprod-import-url').val().trim(); if (!file && !url) { alert(seedprod_admin.strings.select_file_or_url); return; } // Check if theme templates already exist $.post(ajaxurl, { action: 'seedprod_lite_v2_check_existing_theme', nonce: seedprod_admin.v2_nonce }, function(response) { if (response.success && response.data.has_templates) { // Show confirmation dialog if (confirm(seedprod_admin.strings.theme_file_import_confirm)) { // User chose to delete existing and import performThemeImport(file, url, true); } else { // User cancelled - do nothing return; } } else { // No existing templates, proceed normally performThemeImport(file, url, false); } }); }); // Reset import form function resetImportForm() { $('#seedprod-import-file').val(''); $('.seedprod-file-name').text(''); $('#seedprod-import-url').val(''); $('#seedprod-import-theme-btn').prop('disabled', true); $('.seedprod-import-status, .seedprod-export-status').hide(); } } /** * Initialize Landing Pages Import/Export Modal */ function initLandingImportExportModal() { var $modal = $('#seedprod-landing-import-export-modal'); if (!$modal.length) return; // Open modal when button is clicked $('#seedprod-landing-import-export-btn').on('click', function(e) { e.preventDefault(); $modal.fadeIn(200); }); // Close modal $modal.find('.seedprod-modal-close, .seedprod-modal-cancel').on('click', function() { $modal.fadeOut(200); resetLandingImportForm(); }); // Close modal when clicking on overlay $modal.on('click', function(e) { if (e.target === this) { $modal.fadeOut(200); resetLandingImportForm(); } }); // Tab switching $modal.find('.seedprod-tab-button').on('click', function() { var $this = $(this); var tab = $this.data('tab'); // Update active tab button $modal.find('.seedprod-tab-button').removeClass('active'); $this.addClass('active'); // Show corresponding tab content $modal.find('.seedprod-tab-content').hide(); $modal.find('#seedprod-landing-' + tab + '-tab').show(); }); // Handle page selection change $('#seedprod-export-page-select').on('change', function() { var selectedValue = $(this).val(); var $btnText = $('#seedprod-export-landing-btn .button-text'); if (selectedValue === 'all') { $btnText.text(seedprod_admin.strings.export_all_landing_pages || 'Export All Landing Pages'); } else { $btnText.text(seedprod_admin.strings.export_selected_page || 'Export Selected Page'); } }); // Export functionality $('#seedprod-export-landing-btn').off('click.seedprod-export').on('click.seedprod-export', function() { var $btn = $(this); // Prevent multiple simultaneous exports if ($btn.prop('disabled')) { return false; } var $spinner = $btn.find('.spinner'); var $text = $btn.find('.button-text'); var $status = $('.seedprod-export-status'); var $select = $('#seedprod-export-page-select'); var selectedPageId = $select.val(); var selectedOption = $select.find('option:selected'); var pageType = selectedOption.data('ptype') || ''; // Show loading state $btn.prop('disabled', true); $spinner.show(); var originalText = $text.text(); $text.text(seedprod_admin.strings.loading || 'Processing...'); $status.show().find('.notice').removeClass('notice-error notice-success').addClass('notice-info').find('p').text(seedprod_admin.strings.preparing_export || 'Preparing export file...'); // Prepare AJAX data var ajaxData = { action: 'seedprod_lite_v2_export_landing_pages', nonce: seedprod_admin.v2_nonce }; // Add page_id parameter if a specific page is selected if (selectedPageId !== 'all') { ajaxData.page_id = selectedPageId; // Add ptype parameter for special pages if (pageType) { ajaxData.ptype = pageType; } } // Make AJAX request $.ajax({ url: ajaxurl, type: 'POST', data: ajaxData, success: function(response) { $btn.prop('disabled', false); $spinner.hide(); $text.text(originalText); if (response.success && response.data && response.data.filedata) { // Show success message $status.show().find('.notice').removeClass('notice-info notice-error').addClass('notice-success').find('p').text(seedprod_admin.strings.export_completed || 'Export completed successfully! Starting download...'); // Trigger download from inline base64 data via Blob URL. seedprod_lite_v2_download_base64_file(response.data.filedata, response.data.filename || 'seedprod-page-export.zip'); // Hide success message after 3 seconds setTimeout(function() { $status.fadeOut(); }, 3000); } else { // Show error message var errorMsg = (response.data && response.data.message) ? response.data.message : (seedprod_admin.strings.export_failed || 'Export failed. Please try again.'); $status.show().find('.notice').removeClass('notice-info notice-success').addClass('notice-error').find('p').text(errorMsg); } }, error: function(xhr, status, error) { $btn.prop('disabled', false); $spinner.hide(); $text.text(originalText); var errorMsg = seedprod_admin.strings.export_failed || 'Export failed. Please try again.'; if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { errorMsg = xhr.responseJSON.data.message; } $status.show().find('.notice').removeClass('notice-info notice-success').addClass('notice-error').find('p').text(errorMsg); } }); }); // File selection $('#seedprod-landing-select-file-btn').off('click.seedprod-landing').on('click.seedprod-landing', function(e) { e.preventDefault(); e.stopPropagation(); var $fileInput = $('#seedprod-landing-import-file'); $fileInput.click(); }); // Handle file selection var fileChangeTimer; $('#seedprod-landing-import-file').off('change.seedprod-landing').on('change.seedprod-landing', function() { // Debounce the change event to prevent multiple rapid fires clearTimeout(fileChangeTimer); var $this = $(this); fileChangeTimer = setTimeout(function() { var file = $this[0].files[0]; if (file) { // Validate file type if (!file.name.toLowerCase().endsWith('.zip')) { alert(seedprod_admin.strings.invalid_file_type || 'Please select a valid ZIP file.'); $this.val(''); // Clear the invalid selection $('.seedprod-file-name').text(''); $('#seedprod-import-landing-btn').prop('disabled', true); return; } $('.seedprod-file-name').text(file.name); $('#seedprod-import-landing-btn').prop('disabled', false); $('#seedprod-landing-import-url').val(''); // Clear URL field // Hide any previous error messages $('.seedprod-import-status').hide(); } else { // File selection was cancelled or cleared $('.seedprod-file-name').text(''); if (!$('#seedprod-landing-import-url').val().trim()) { $('#seedprod-import-landing-btn').prop('disabled', true); } } }, 100); // 100ms debounce }); // Handle URL input $('#seedprod-landing-import-url').on('input', function() { var url = $(this).val().trim(); if (url) { $('#seedprod-import-landing-btn').prop('disabled', false); // Clear file selection $('#seedprod-landing-import-file').val(''); $('.seedprod-file-name').text(''); } else if (!$('#seedprod-landing-import-file').val()) { $('#seedprod-import-landing-btn').prop('disabled', true); } }); // Import functionality $('#seedprod-import-landing-btn').on('click', function(e) { e.preventDefault(); e.stopPropagation(); var $btn = $(this); // Prevent duplicate clicks if ($btn.hasClass('importing')) { return; } var $spinner = $btn.find('.spinner'); var $text = $btn.find('.button-text'); var $status = $('.seedprod-import-status'); var file = $('#seedprod-landing-import-file')[0].files[0]; var url = $('#seedprod-landing-import-url').val().trim(); if (!file && !url) { alert(seedprod_admin.strings.select_file_or_url); return; } // Show loading state and mark as importing $btn.addClass('importing').prop('disabled', true); $spinner.show(); $text.text(seedprod_admin.strings.loading || 'Importing...'); $status.show().find('.notice').removeClass('notice-error notice-success notice-warning').addClass('notice-info').find('p').text(seedprod_admin.strings.processing_import || 'Processing import...'); if (file) { // File upload var formData = new FormData(); formData.append('action', 'seedprod_lite_v2_import_landing_pages'); formData.append('nonce', seedprod_admin.v2_nonce); formData.append('seedprod_landing_files', file); $.ajax({ url: ajaxurl, type: 'POST', data: formData, processData: false, contentType: false, success: function(response) { $btn.removeClass('importing').prop('disabled', false); $spinner.hide(); $text.text('Import Landing Pages'); console.log('Import response:', response); // The import function returns just true/false, not {success: true} if (response === true || response.success) { $status.find('.notice').removeClass('notice-info').addClass('notice-success').find('p').text(seedprod_admin.strings.import_success || 'Import completed successfully!'); var warnings = (response && response.data && response.data.warnings) ? response.data.warnings : []; seedprodRenderImportWarnings(warnings, $status); var reloadDelay = warnings.length ? 8000 : 2000; setTimeout(function() { window.location.reload(); }, reloadDelay); } else if (response === false) { $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(seedprod_admin.strings.error_occurred || 'An error occurred. Please try again.'); } else { var errorMessage = response.data || 'Import failed. Please try again.'; if (typeof response.data === 'object' && response.data.message) { errorMessage = response.data.message; } $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(errorMessage); } }, error: function(xhr, status, error) { $btn.removeClass('importing').prop('disabled', false); $spinner.hide(); $text.text('Import Landing Pages'); console.log('Import error:', error); var errorMessage = 'Import failed: ' + (error || status || 'Unknown error'); if (xhr.responseJSON && xhr.responseJSON.data) { errorMessage = xhr.responseJSON.data; } $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text(errorMessage); } }); } else { // URL import is not supported for landing pages (UI should be hidden) // This shouldn't be reached as the URL import section is hidden $btn.removeClass('importing').prop('disabled', false); $spinner.hide(); $text.text('Import Landing Pages'); $status.find('.notice').removeClass('notice-info').addClass('notice-error').find('p').text('Please select a file to import.'); } }); // Reset import form function resetLandingImportForm() { $('#seedprod-landing-import-file').val(''); $('.seedprod-file-name').text(''); $('#seedprod-landing-import-url').val(''); $('#seedprod-import-landing-btn').prop('disabled', true); $('.seedprod-import-status, .seedprod-export-status').hide(); } } // Initialize Website Builder features if ($('.seedprod-website-builder-page').length) { initWebsiteBuilderThemeToggle(); initThemeTemplatesTable(); initAddNewTemplateModal(); initEditConditionsModal(); initImportExportModal(); } else { // Also try to initialize on document ready as a fallback $(document).ready(function() { if ($('.seedprod-website-builder-page').length) { initWebsiteBuilderThemeToggle(); initThemeTemplatesTable(); initAddNewTemplateModal(); initEditConditionsModal(); initImportExportModal(); } }); } // Initialize Landing Pages features if ($('.seedprod-landing-pages-page').length) { initLandingImportExportModal(); } else { // Also try to initialize on document ready as a fallback $(document).ready(function() { if ($('.seedprod-landing-pages-page').length) { initLandingImportExportModal(); } }); } /** * Review Notice Handlers (V2 Admin) * Handles the 3-step review request flow */ $(document).on('click', '.seedprod-v2-review-notice .seedprod-dismiss-review-notice, .seedprod-v2-review-notice button', function(event) { if (!$(this).hasClass('seedprod-review-out')) { event.preventDefault(); } // Check if this is a permanent dismissal (already reviewed) var isPermanent = $(this).hasClass('seedprod-dismiss-review-notice-permanent'); $.post(ajaxurl, { action: 'seedprod_v2_review_dismiss', permanent: isPermanent ? 'true' : 'false' }); $('.seedprod-v2-review-notice').fadeOut(); }); // Handle step switching in review notice $(document).on('click', '.seedprod-v2-review-notice .seedprod-review-switch-step', function(e) { e.preventDefault(); var targetStep = $(this).attr('data-step'); if (targetStep) { var $notice = $(this).closest('.seedprod-v2-review-notice'); var $targetStepDiv = $notice.find('.seedprod-review-step-' + targetStep); if ($targetStepDiv.length > 0) { $notice.find('.seedprod-review-step:visible').fadeOut(function() { $targetStepDiv.fadeIn(); }); } } }); // Handle "Ask me later" link $(document).on('click', '.seedprod-v2-review-notice .seedprod-review-dismiss-link', function(e) { e.preventDefault(); $.post(ajaxurl, { action: 'seedprod_v2_review_dismiss', permanent: 'false' }); $('.seedprod-v2-review-notice').fadeOut(); }); })( jQuery );
Save File
Cancel