/** * Dashboard-inspired JavaScript enhancements for Metasync plugin * Adds modern interactions and animations matching the dashboard design */ (function ($) { 'use strict'; $(document).ready(function () { // Only run on plugin pages if (!$('.metasync-dashboard-wrap').length) { return; } // Fix plugin admin styles (scoped) fixPluginAdminStyles(); // Add loading states to buttons enhanceButtonInteractions(); // Add smooth transitions to cards // enhanceCardAnimations(); // Disabled - was too distracting in settings pages // Enhance form validation enhanceFormValidation(); // Add dashboard-style notifications enhanceDashboardNotifications(); // Enhanced API response handling for Google Console enhanceGoogleConsoleResponse(); // Enhanced Search Atlas Connect handling enhanceSearchAtlasConnect(); // Integrate with existing connect functions integrateConnectWithDashboard(); // Add tooltips to stat cards addStatCardTooltips(); // Initialize progress bars initializeProgressBars(); // Fix stat card text overflow fixStatCardTextOverflow(); // Handle responsive layout handleResponsiveLayout(); }); /** * Enhance button interactions with loading states - SCOPED TO PLUGIN */ function enhanceButtonInteractions() { $('.metasync-dashboard-wrap .button-primary, .metasync-dashboard-wrap .button-secondary').on('click', function (e) { const $button = $(this); // Don't add loading to certain buttons if ($button.hasClass('no-loading') || $button.attr('type') === 'submit') { return; } // Add loading state $button.addClass('dashboard-loading'); $button.prop('disabled', true); // Remove loading state after animation setTimeout(() => { $button.removeClass('dashboard-loading'); $button.prop('disabled', false); }, 2000); }); } /** * Add hover animations to dashboard cards - SCOPED TO PLUGIN */ function enhanceCardAnimations() { $('.metasync-dashboard-wrap .dashboard-card').hover( function () { $(this).addClass('card-hover'); }, function () { $(this).removeClass('card-hover'); } ); // Add stagger animation to stat cards $('.metasync-dashboard-wrap .dashboard-stat-card').each(function (index) { $(this).css('animation-delay', (index * 0.1) + 's'); $(this).addClass('fade-in-up'); }); } /** * Enhance form validation with better UX - SCOPED TO PLUGIN */ function enhanceFormValidation() { $('.metasync-dashboard-wrap input, .metasync-dashboard-wrap textarea, .metasync-dashboard-wrap select').on('blur', function () { const $input = $(this); if ($input.is(':invalid')) { $input.addClass('input-error'); showInputError($input, 'Please check this field'); } else { $input.removeClass('input-error'); hideInputError($input); } }); $('.metasync-dashboard-wrap input, .metasync-dashboard-wrap textarea, .metasync-dashboard-wrap select').on('input', function () { const $input = $(this); if ($input.hasClass('input-error') && $input.is(':valid')) { $input.removeClass('input-error'); hideInputError($input); } }); } /** * Show input error with dashboard styling */ function showInputError($input, message) { const errorId = 'error-' + Math.random().toString(36).substr(2, 9); if ($input.siblings('.input-error-message').length === 0) { $input.after(`
`); setTimeout(() => { $(`#${errorId}`).css({ opacity: 1, transform: 'translateY(0)' }); }, 10); } } /** * Hide input error */ function hideInputError($input) { const $error = $input.siblings('.input-error-message'); $error.css({ opacity: 0, transform: 'translateY(-10px)' }); setTimeout(() => { $error.remove(); }, 300); } /** * Enhance dashboard notifications - SCOPED TO PLUGIN */ function enhanceDashboardNotifications() { $('.metasync-dashboard-wrap .notice').each(function () { const $notice = $(this); $notice.addClass('dashboard-notice-enhanced'); // Add close button if (!$notice.find('.notice-dismiss').length) { $notice.append(` `); } }); // Handle notice dismiss ONLY within plugin pages $('.metasync-dashboard-wrap').on('click', '.notice-dismiss', function () { $(this).closest('.notice').fadeOut(300); }); } /** * Enhanced Google Console API response handling */ function enhanceGoogleConsoleResponse() { const $responseDiv = $('#metasync-giapi-response'); if ($responseDiv.length === 0) { return; } // Show response with animation when data is received const originalShow = $responseDiv.show; $responseDiv.show = function () { $responseDiv.css('opacity', 0).slideDown(300).animate({ opacity: 1 }, 300); return this; }; // Enhanced send button for Google Console $('#metasync-btn-send').on('click', function () { const $button = $(this); const originalText = $button.text(); $button.html('đ Sending...').prop('disabled', true); // Simulate API call progress (replace with actual API integration) let progress = 0; const progressInterval = setInterval(() => { progress += 10; if (progress >= 100) { clearInterval(progressInterval); $button.html(originalText).prop('disabled', false); } }, 200); }); } /** * Add tooltips to stat cards - SCOPED TO PLUGIN */ function addStatCardTooltips() { $('.metasync-dashboard-wrap .dashboard-stat-card').each(function () { const $card = $(this); const label = $card.find('.dashboard-stat-label').text(); const value = $card.find('.dashboard-stat-value').text(); $card.attr('title', `${label}: ${value}`) .addClass('dashboard-tooltip'); }); } /** * Initialize animated progress bars - SCOPED TO PLUGIN */ function initializeProgressBars() { $('.metasync-dashboard-wrap .dashboard-progress-bar').each(function () { const $bar = $(this); const width = $bar.data('width') || '0%'; // Animate progress bar on scroll into view const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { setTimeout(() => { $bar.css('width', width); }, 500); observer.unobserve(entry.target); } }); }); observer.observe($bar[0]); }); } /** * Add success animation to forms after submission */ function addSuccessAnimation($element) { $element.addClass('metasync-sa-connect-success-animation'); setTimeout(() => { $element.removeClass('metasync-sa-connect-success-animation'); }, 600); } /** * Enhanced tab navigation - SCOPED TO PLUGIN */ $('.metasync-dashboard-wrap .metasync-nav-tab, .metasync-dashboard-wrap .nav-tab').on('click', function (e) { const $tab = $(this); // Add loading effect $tab.addClass('tab-loading'); // Remove loading after page load setTimeout(() => { $tab.removeClass('tab-loading'); }, 1000); }); /** * Set active navigation tab based on current page */ function setActiveNavTab() { const currentUrl = window.location.href; const $navTabs = $('.metasync-dashboard-wrap .metasync-nav-tab'); $navTabs.each(function () { const $tab = $(this); const tabHref = $tab.attr('href'); if (tabHref && currentUrl.includes(tabHref.split('?')[1])) { $navTabs.removeClass('active'); $tab.addClass('active'); } }); } // Set active tab on page load setActiveNavTab(); /** * Add keyboard navigation for accessibility - SCOPED TO PLUGIN */ $('.metasync-dashboard-wrap .dashboard-card').attr('tabindex', '0').on('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { $(this).click(); } }); /** * Dark mode toggle functionality (if needed) */ function addDarkModeToggle() { // This can be expanded if manual dark mode toggle is needed // Currently the dashboard is always dark themed } /** * Responsive navigation improvements */ function enhanceResponsiveNavigation() { const $navTabs = $('.nav-tab-wrapper'); if ($navTabs.length && window.innerWidth < 768) { $navTabs.addClass('nav-mobile'); } $(window).on('resize', function () { if (window.innerWidth < 768) { $navTabs.addClass('nav-mobile'); } else { $navTabs.removeClass('nav-mobile'); } }); } // Initialize responsive enhancements enhanceResponsiveNavigation(); /** * Fix WordPress admin styles - SCOPED TO PLUGIN ONLY */ function fixPluginAdminStyles() { const $pluginWrap = $('.metasync-dashboard-wrap'); if ($pluginWrap.length === 0) { return; } // Handle WordPress notices ONLY within plugin pages $pluginWrap.find('.notice, .error, .updated').each(function () { $(this).addClass('dashboard-notice'); }); // Fix nav tabs background ONLY within plugin pages $pluginWrap.find('.nav-tab-wrapper').css('background', 'transparent'); } /** * Fix stat card text overflow issues - SCOPED TO PLUGIN */ function fixStatCardTextOverflow() { $('.metasync-dashboard-wrap .dashboard-stat-card').each(function () { const $card = $(this); const $value = $card.find('.dashboard-stat-value'); const $label = $card.find('.dashboard-stat-label'); // Handle long theme names or values if ($value.text().length > 20) { $value.addClass('long-text'); $card.attr('title', $value.text()); } // Adjust font size based on text length if ($value.text().length > 15) { $value.css('font-size', '1.4rem'); } if ($value.text().length > 25) { $value.css('font-size', '1.2rem'); } }); } /** * Handle responsive layout changes - SCOPED TO PLUGIN */ function handleResponsiveLayout() { function adjustLayout() { const $pluginWrap = $('.metasync-dashboard-wrap'); if ($pluginWrap.length === 0) { return; } const windowWidth = $(window).width(); if (windowWidth < 768) { $pluginWrap.find('.dashboard-stats').addClass('mobile-layout'); $pluginWrap.find('.metasync-sa-connect-buttons').addClass('mobile-buttons'); } else { $pluginWrap.find('.dashboard-stats').removeClass('mobile-layout'); $pluginWrap.find('.metasync-sa-connect-buttons').removeClass('mobile-buttons'); } if (windowWidth < 1200) { $pluginWrap.find('.dashboard-card').addClass('compact-layout'); } else { $pluginWrap.find('.dashboard-card').removeClass('compact-layout'); } } // Run on load adjustLayout(); // Run on resize $(window).on('resize', debounce(adjustLayout, 250)); } /** * Debounce function for performance */ function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * Enhanced Search Atlas Connect handling - SCOPED TO PLUGIN * * Handles 1-click connect to retrieve Search Atlas API key and Otto UUID. * Does NOT create WordPress login sessions. */ function enhanceSearchAtlasConnect() { const $connectContainer = $('.metasync-dashboard-wrap .metasync-sa-connect-container'); const $connectBtn = $('.metasync-dashboard-wrap #connect-searchatlas-btn'); const $retryBtn = $('.metasync-dashboard-wrap .metasync-sa-connect-retry-btn'); if ($connectContainer.length === 0) { return; } // Handle connect button clicks $connectBtn.on('click', function () { const $btn = $(this); const originalText = $btn.text(); // Add loading state $btn.prop('disabled', true) .html(' Opening Authentication...') .addClass('dashboard-loading'); // Show progress indicator showConnectProgress(); // Start progress timer startConnectTimer(); // Remove any existing status messages $('.metasync-sa-connect-status').remove(); }); // Handle retry button clicks $(document).on('click', '.metasync-sa-connect-retry-btn', function () { const $btn = $(this); // Remove status messages $('.metasync-sa-connect-status').remove(); // Re-enable connect button $connectBtn.prop('disabled', false) .html('đ Re-authenticate with ' + (window.MetasyncConfig && window.MetasyncConfig.pluginName ? window.MetasyncConfig.pluginName : 'Search Atlas')) .removeClass('dashboard-loading'); // Hide progress $('.metasync-sa-connect-progress').fadeOut(300); }); // Handle authentication tips toggle for custom elements only (exclude summary elements) $(document).on('click', '.metasync-sa-connect-tips-toggle:not(summary)', function (e) { const $toggle = $(this); const $content = $toggle.siblings('.metasync-sa-connect-tips-content'); const $icon = $toggle.find('.tips-icon'); $content.slideToggle(300); $icon.text($content.is(':visible') ? 'âŧ' : 'âļ'); }); // Handle details element with custom animation $(document).on('click', 'details.metasync-sa-connect-tips summary', function (e) { const $details = $(this).closest('details'); if ($details.prop('open')) { // Is open, about to close e.preventDefault(); $details.find('> div').slideUp(400, () => { $details.removeAttr('open'); }); } else { // Is closed, about to open. Let it open, then animate. // The 'toggle' event is another way to handle this. e.preventDefault(); $details.attr('open', 'open'); $details.find('> div').hide(); $details.find('> div').slideDown(400); } }); } /** * Show connect progress indicator */ function showConnectProgress() { const progressHTML = ` `; $('.metasync-sa-connect-container').append(progressHTML); } /** * Start connect timer and progress animation */ function startConnectTimer() { let elapsed = 0; const maxTime = 300; // 5 minutes in seconds const timer = setInterval(() => { elapsed += 1; const remaining = maxTime - elapsed; const progressPercent = (elapsed / maxTime) * 100; // Update progress bar $('.metasync-sa-connect-progress-fill').css('width', progressPercent + '%'); // Update time display const elapsedMin = Math.floor(elapsed / 60); const remainingMin = Math.floor(remaining / 60); $('.metasync-sa-connect-progress-time').text(`${elapsedMin}min elapsed, ${remainingMin}min remaining`); // Check if time is up if (elapsed >= maxTime) { clearInterval(timer); showConnectTimeout(); } }, 1000); // Store timer reference for cleanup $('.metasync-sa-connect-container').data('connect-timer', timer); } /** * Show connect timeout message */ function showConnectTimeout() { showConnectStatus('error', 'Authentication Timeout', 'The authentication window timed out. You can try again when you\'re ready.'); } /** * Show connect status message */ function showConnectStatus(type, title, message) { const iconMap = { success: 'â ', error: 'â ī¸', info: 'âšī¸', warning: 'â ī¸' }; const statusHTML = ` `; // Remove existing status $('.metasync-sa-connect-status').remove(); // Add new status $('.metasync-sa-connect-container').append(statusHTML); // Clear timer and progress const timer = $('.metasync-sa-connect-container').data('connect-timer'); if (timer) { clearInterval(timer); } // Reset button state $('#connect-searchatlas-btn').prop('disabled', false) .html('đ Re-authenticate with ' + (window.MetasyncConfig && window.MetasyncConfig.pluginName ? window.MetasyncConfig.pluginName : 'Search Atlas')) .removeClass('dashboard-loading'); // Hide progress after delay setTimeout(() => { $('.metasync-sa-connect-progress').fadeOut(300); }, 1000); } /** * Integrate with existing Search Atlas connect functions */ function integrateConnectWithDashboard() { // Override existing SSO status display to use dashboard styling if (typeof window.showConnectStatus !== 'undefined') { const originalShowConnectStatus = window.showConnectStatus; window.showConnectStatus = function (type, title, message, actions) { // If we're on a dashboard page, use our enhanced styling if ($('.metasync-dashboard-wrap').length > 0) { showConnectStatus(type, title, message); } else { // Fallback to original function for non-dashboard pages originalShowConnectStatus.call(this, type, title, message, actions); } }; } // Enhance existing SSO containers with dashboard styling $('.metasync-sa-connect-container').each(function () { if (!$(this).closest('.metasync-dashboard-wrap').length) { return; // Only enhance containers within dashboard pages } // Add dashboard classes to existing elements $(this).addClass('dashboard-enhanced'); // Style existing status messages $(this).find('.metasync-sa-connect-status').each(function () { $(this).addClass('dashboard-styled'); }); // Style existing progress elements $(this).find('.metasync-sa-connect-progress').each(function () { $(this).addClass('dashboard-styled'); }); // Style existing authentication tips (PHP-generated details element) // Check for both class-based tips AND details elements to prevent duplication const hasExistingTips = $(this).find('.metasync-sa-connect-tips').length > 0 || $(this).find('details summary').filter(function () { return $(this).text().includes('Authentication Tips'); }).length > 0; if (!hasExistingTips) { // No existing tips found, this shouldn't happen but just in case console.log('No authentication tips found in connect container'); } else { // Style existing PHP-generated details element $(this).find('details').each(function () { const $details = $(this); if ($details.find('summary').text().includes('Authentication Tips')) { // Add dashboard styling to existing tips $details.addClass('metasync-sa-connect-tips dashboard-styled'); $details.find('div').addClass('metasync-sa-connect-tips-content'); } }); } }); // Monitor for dynamically added SSO elements const observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { mutation.addedNodes.forEach(function (node) { if (node.nodeType === 1) { // Element node const $node = $(node); // Style new SSO status messages if ($node.hasClass('metasync-sa-connect-status')) { $node.addClass('dashboard-styled'); } // Style new progress indicators if ($node.hasClass('metasync-sa-connect-progress')) { $node.addClass('dashboard-styled'); } // Check for nested SSO elements $node.find('.metasync-sa-connect-status, .metasync-sa-connect-progress').addClass('dashboard-styled'); } }); }); }); // Start observing $('.metasync-dashboard-wrap').each(function () { observer.observe(this, { childList: true, subtree: true }); }); } // Expose functions globally for SSO integration window.metasyncDashboard = { showConnectStatus: showConnectStatus, showConnectProgress: showConnectProgress, startConnectTimer: startConnectTimer, integrateConnectWithDashboard: integrateConnectWithDashboard }; })(jQuery); // CSS animations for JavaScript enhancements const additionalCSS = ` `; // Inject additional CSS document.head.insertAdjacentHTML('beforeend', additionalCSS);