/** * wpForo AI Features - Admin JavaScript * * Handles interactive elements on the AI Features admin page including: * - API key reveal/hide functionality * - Disconnect service confirmation dialog * - Form submission with loading states * * @since 3.0.0 */ (function($) { 'use strict'; /** * Main AI Features Admin object */ const WpForoAI = { // Flag to prevent multiple initializations initialized: false, /** * Initialize all functionality */ init: function() { // Prevent multiple initializations if (this.initialized) { console.log('WpForoAI already initialized, skipping...'); return; } this.initialized = true; this.bindEvents(); this.initTooltips(); this.initRAGFeatures(); this.initTagSuggest(); this.initCharCounters(); this.initBotUserSearch(); this.checkPostPurchaseRefresh(); }, /** * Bind event handlers */ bindEvents: function() { // Unbind all events first to prevent duplicates $(document).off('click', '.wpforo-ai-reveal-key'); $(document).off('click', '.wpforo-ai-disconnect-btn'); $(document).off('click', '.wpforo-ai-disconnect-purge-btn'); $(document).off('submit', '.wpforo-ai-wrap form'); $(document).off('click', '.wpforo-ai-copy-btn'); $(document).off('click', '.wpforo-ai-upgrade-btn'); $(document).off('click', '.wpforo-ai-buy-credits-btn'); $(document).off('click', '.wpforo-ai-features-accordion .accordion-header'); $(document).off('click', '.wpforo-ai-activate-license-btn'); $(document).off('click', '.wpforo-ai-activate-paddle-txn-btn'); $(document).off('click', '.wpforo-ai-bonus-credits-btn.eligible'); $(document).off('click', '.wpforo-ai-legal-link'); $(document).off('click', '.wpforo-ai-modal-close, .wpforo-ai-modal-close-btn, .wpforo-ai-modal-overlay'); // Reveal/hide API key $(document).on('click', '.wpforo-ai-reveal-key', this.toggleApiKeyVisibility.bind(this)); // Disconnect service button $(document).on('click', '.wpforo-ai-disconnect-btn', this.showDisconnectDialog.bind(this)); // Disconnect and remove all data button $(document).on('click', '.wpforo-ai-disconnect-purge-btn', this.showDisconnectPurgeDialog.bind(this)); // Add loading state to form submissions (use event delegation to prevent multiple handlers) $(document).on('submit', '.wpforo-ai-wrap form', this.handleFormSubmit.bind(this)); // Copy to clipboard functionality (if needed in future) $(document).on('click', '.wpforo-ai-copy-btn', this.copyToClipboard.bind(this)); // Checkout - Upgrade buttons (routes to Paddle or Freemius based on selected provider) $(document).on('click', '.wpforo-ai-upgrade-btn', this.handleUpgradeClick.bind(this)); // Checkout - Credit pack purchase buttons (routes to Paddle or Freemius) $(document).on('click', '.wpforo-ai-buy-credits-btn', this.handleCreditPackClick.bind(this)); // Payment provider toggle $(document).on('change', 'input[name="wpforo_ai_payment_provider"]', this.handleProviderChange.bind(this)); // Features accordion toggle $(document).on('click', '.wpforo-ai-features-accordion .accordion-header', this.toggleAccordion.bind(this)); // License activation button $(document).on('click', '.wpforo-ai-activate-license-btn', this.activateLicense.bind(this)); // Paddle transaction activation button $(document).on('click', '.wpforo-ai-activate-paddle-txn-btn', this.activatePaddleTransaction.bind(this)); // Bonus credits request button $(document).on('click', '.wpforo-ai-bonus-credits-btn.eligible', this.requestBonusCredits.bind(this)); // Legal document links $(document).on('click', '.wpforo-ai-legal-link', this.openLegalModal.bind(this)); // Close legal modal $(document).on('click', '.wpforo-ai-modal-close, .wpforo-ai-modal-close-btn, .wpforo-ai-modal-overlay', this.closeLegalModal.bind(this)); // Close modal with Escape key $(document).on('keydown', this.handleModalKeydown.bind(this)); // Terms checkbox validation $(document).on('submit', '#wpforo-ai-connect-form', this.validateTermsAgreement.bind(this)); }, /** * Toggle accordion panel */ toggleAccordion: function(e) { e.preventDefault(); const $header = $(e.currentTarget); const $content = $header.next('.accordion-content'); const isExpanded = $header.attr('aria-expanded') === 'true'; if (isExpanded) { // Collapse $header.attr('aria-expanded', 'false'); $content.slideUp(300); } else { // Expand $header.attr('aria-expanded', 'true'); $content.slideDown(300); } }, /** * Open legal document modal */ openLegalModal: function(e) { e.preventDefault(); const $link = $(e.currentTarget); const documentType = $link.data('document'); const $modal = $('#wpforo-ai-legal-modal'); const $title = $('#wpforo-ai-modal-title'); const $content = $('#wpforo-ai-modal-content'); // Set title based on document type if (documentType === 'terms') { $title.text('Terms of Service'); } else if (documentType === 'privacy') { $title.text('Privacy Policy'); } // Show loading state $content.html('
Loading document...
'); $modal.show(); $('body').addClass('wpforo-ai-modal-open'); // Load document content via AJAX $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'wpforo_ai_get_legal_document', document: documentType, nonce: wpforoAIAdmin.nonce }, success: function(response) { if (response.success && response.data.content) { $content.html(response.data.content); } else { $content.html('

Error loading document. Please try again.

'); } }, error: function() { $content.html('

Error loading document. Please try again.

'); } }); }, /** * Close legal document modal */ closeLegalModal: function(e) { if (e) { e.preventDefault(); } const $modal = $('#wpforo-ai-legal-modal'); $modal.hide(); $('body').removeClass('wpforo-ai-modal-open'); }, /** * Handle keyboard events for modal */ handleModalKeydown: function(e) { if (e.key === 'Escape' && $('#wpforo-ai-legal-modal').is(':visible')) { this.closeLegalModal(); } }, /** * Validate terms agreement before form submission */ validateTermsAgreement: function(e) { const $checkbox = $('#wpforo-ai-agree-terms'); if (!$checkbox.is(':checked')) { e.preventDefault(); alert('Please read and agree to the Terms of Service and Privacy Policy before connecting.'); $checkbox.focus(); return false; } return true; }, /** * Toggle API key visibility */ toggleApiKeyVisibility: function(e) { e.preventDefault(); const $button = $(e.currentTarget); const $keyElement = $('.wpforo-ai-key-masked'); const isRevealed = $button.data('revealed') === true; if (!isRevealed) { // Show confirmation before revealing if (!confirm('Are you sure you want to reveal your API key? Make sure no one is looking over your shoulder.')) { return; } // Get full key from WordPress options via AJAX this.fetchFullApiKey(function(fullKey) { if (fullKey) { $keyElement.text(fullKey); $button.data('revealed', true); $button.html(' Hide'); } }); } else { // Hide the key again this.fetchMaskedApiKey(function(maskedKey) { $keyElement.text(maskedKey); $button.data('revealed', false); $button.html(' Reveal'); }); } }, /** * Fetch full API key via AJAX */ fetchFullApiKey: function(callback) { // For now, we'll use a placeholder since AJAX endpoint isn't implemented // TODO: Implement AJAX endpoint for secure key retrieval const $keyElement = $('.wpforo-ai-key-masked'); const maskedKey = $keyElement.text(); // This is a temporary solution - in production, fetch from server const mockFullKey = maskedKey.replace('***', 'XXXXXXXXXXXXXXXX'); callback(mockFullKey); }, /** * Fetch masked API key */ fetchMaskedApiKey: function(callback) { const $keyElement = $('.wpforo-ai-key-masked'); const currentText = $keyElement.text(); const prefix = currentText.substring(0, 6); const maskedKey = prefix + '***'; callback(maskedKey); }, /** * Show disconnect service confirmation dialog */ showDisconnectDialog: function(e) { e.preventDefault(); const $form = $('#wpforo-ai-disconnect-form'); if (!$form.length) { console.error('Disconnect form not found'); return; } // Use WordPress-style dialog if available if (typeof wp !== 'undefined' && wp.media) { // TODO: Implement custom modal with wp.media this.showNativeDisconnectDialog($form); } else { this.showNativeDisconnectDialog($form); } }, /** * Show native browser confirm dialog for disconnect */ showNativeDisconnectDialog: function($form) { const confirmMessage = '⚠️ WARNING: This will disconnect your forum from wpForo AI service.\n\n' + '⚠️ If you have an active subscription plan, please cancel it before disconnecting. Disconnecting does NOT cancel your subscription.\n\n' + '• Your credits will be preserved\n' + '• Your indexed content will be deleted after 30 days\n' + '• You can reconnect anytime with the same site URL\n\n' + 'Are you absolutely sure you want to disconnect?'; if (!confirm(confirmMessage)) { return; } // Ask for optional reason (user can click Cancel and still proceed) const reason = prompt('Optional: Tell us why you\'re disconnecting (helps us improve):'); // Set values in form $form.find('input[name="confirm"]').prop('checked', true); if (reason && reason.trim()) { $form.find('textarea[name="reason"]').val(reason.trim()); } // Submit form using native DOM method (works better in Firefox after preventDefault) $form[0].submit(); }, /** * Show disconnect and remove all data confirmation dialog */ showDisconnectPurgeDialog: function(e) { e.preventDefault(); const $form = $('#wpforo-ai-disconnect-purge-form'); if (!$form.length) { console.error('Disconnect purge form not found'); return; } const confirmMessage = '⚠️ WARNING: This will PERMANENTLY DELETE ALL your data from gVectors AI servers.\n\n' + '⚠️ If you have an active subscription plan, please cancel it before disconnecting. Disconnecting does NOT cancel your subscription.\n\n' + '• All indexed content and embeddings will be deleted immediately\n' + '• Your credits will NOT be preserved\n' + '• Your tenant account will be removed\n' + '• This action CANNOT be undone\n\n' + 'Are you absolutely sure you want to delete all data?'; if (!confirm(confirmMessage)) { return; } // Double confirmation for destructive action if (!confirm('This is your final confirmation. All data will be permanently deleted. Continue?')) { return; } const reason = prompt('Optional: Tell us why you\'re removing your data (helps us improve):'); $form.find('input[name="confirm"]').prop('checked', true); if (reason && reason.trim()) { $form.find('textarea[name="reason"]').val(reason.trim()); } $form[0].submit(); }, /** * Handle form submission with loading state */ handleFormSubmit: function(e) { const $form = $(e.currentTarget); const $submitButton = $form.find('button[type="submit"]'); // Add loading state to button $submitButton.addClass('loading').prop('disabled', true); // Note: Form will submit normally, this just adds visual feedback // The page will reload after submission completes }, /** * Copy text to clipboard */ copyToClipboard: function(e) { e.preventDefault(); const $button = $(e.currentTarget); const textToCopy = $button.data('copy'); if (!textToCopy) { return; } // Modern clipboard API if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(textToCopy).then(function() { WpForoAI.showCopySuccess($button); }).catch(function(err) { console.error('Failed to copy:', err); WpForoAI.fallbackCopyToClipboard(textToCopy, $button); }); } else { // Fallback for older browsers this.fallbackCopyToClipboard(textToCopy, $button); } }, /** * Fallback copy method for older browsers */ fallbackCopyToClipboard: function(text, $button) { const $temp = $('