PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.5
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.5
2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 All 139 releases
metasync / admin / js / metasync-setup-wizard.js

metasync-setup-wizard.js in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.5, at admin/js/metasync-setup-wizard.js

497 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Setup Wizard JavaScript
3 *
4 * Handles wizard navigation, validation, SSO integration, and imports.
5 *
6 * @package Metasync
7 * @subpackage Metasync/admin/js
8 */
9
10 /* global metasyncWizardData */
11
12 (function ($) {
13 'use strict';
14
15 var MetasyncWizard = {
16 currentStep: 1,
17 totalSteps: 6,
18 state: {},
19 ssoPopup: null,
20 ssoPollingInterval: null,
21
22 /**
23 * Initialize wizard
24 */
25 init: function () {
26 this.bindEvents();
27 this.renderStep(this.currentStep);
28 },
29
30 /**
31 * Bind all event handlers
32 */
33 bindEvents: function () {
34 var self = this;
35
36 // Navigation buttons
37 $('.wizard-btn-next').on('click', function () {
38 self.nextStep();
39 });
40
41 $('.wizard-btn-prev').on('click', function () {
42 self.prevStep();
43 });
44
45 $('.wizard-btn-skip').on('click', function () {
46 self.skipStep();
47 });
48
49 // Progress step indicators (click to jump to completed steps)
50 $('.wizard-progress-step').on('click', function () {
51 var step = parseInt($(this).data('step'));
52 if (self.isStepAccessible(step)) {
53 self.goToStep(step);
54 }
55 });
56
57 // Connection button (reuse existing SSO)
58 $('#wizard-connect-btn').on('click', function () {
59 self.triggerSSO();
60 });
61
62 // Skip connection link
63 $('.wizard-skip-connection').on('click', function (e) {
64 e.preventDefault();
65 self.nextStep();
66 });
67
68 // Import buttons
69 $(document).on('click', '.wizard-import-btn', function () {
70 var plugin = $(this).data('plugin');
71 self.runImport(plugin, $(this));
72 });
73
74 // Import option checkboxes - enable/disable import button
75 $(document).on('change', '.import-option', function () {
76 var $card = $(this).closest('.wizard-import-card');
77 self.updateImportButtonState($card);
78 });
79
80 // Apply recommended SEO settings
81 $('.wizard-apply-recommended').on('click', function () {
82 $('input[name="seo_category_archives"]').prop('checked', true);
83 $('input[name="seo_tag_archives"]').prop('checked', true);
84 $('input[name="seo_date_archives"]').prop('checked', false);
85 $('input[name="seo_author_archives"]').prop('checked', false);
86 });
87
88 // Schema enable/disable
89 $('#schema-enabled').on('change', function () {
90 if ($(this).is(':checked')) {
91 $('.wizard-schema-settings').slideDown();
92 } else {
93 $('.wizard-schema-settings').slideUp();
94 }
95 });
96
97 // Complete button
98 $('.wizard-complete-btn').on('click', function () {
99 self.completeWizard();
100 });
101 },
102
103 /**
104 * Go to next step
105 */
106 nextStep: function () {
107 if (this.validateCurrentStep()) {
108 this.saveStepData();
109 if (this.currentStep < this.totalSteps) {
110 this.currentStep++;
111 this.renderStep(this.currentStep);
112 }
113 }
114 },
115
116 /**
117 * Go to previous step
118 */
119 prevStep: function () {
120 if (this.currentStep > 1) {
121 this.currentStep--;
122 this.renderStep(this.currentStep);
123 }
124 },
125
126 /**
127 * Skip current step
128 */
129 skipStep: function () {
130 // Just go to next step without validation
131 if (this.currentStep < this.totalSteps) {
132 this.currentStep++;
133 this.renderStep(this.currentStep);
134 }
135 },
136
137 /**
138 * Go to specific step
139 */
140 goToStep: function (step) {
141 if (step >= 1 && step <= this.totalSteps) {
142 this.currentStep = step;
143 this.renderStep(step);
144 }
145 },
146
147 /**
148 * Render specific step
149 */
150 renderStep: function (step) {
151 // Hide all steps
152 $('.wizard-step').removeClass('active');
153
154 // Show current step
155 $('.wizard-step[data-step="' + step + '"]').addClass('active');
156
157 // Update progress bar
158 var progress = (step / this.totalSteps) * 100;
159 $('.wizard-progress-bar').css('width', progress + '%');
160
161 // Update step indicators
162 $('.wizard-progress-step').each(function () {
163 var stepNum = parseInt($(this).data('step'));
164 $(this).toggleClass('active', stepNum === step);
165 $(this).toggleClass('completed', stepNum < step);
166 });
167
168 // Update navigation buttons
169 $('.wizard-btn-prev').prop('disabled', step === 1);
170
171 if (step === this.totalSteps) {
172 $('.wizard-btn-next').hide();
173 $('.wizard-btn-skip').hide();
174 } else {
175 $('.wizard-btn-next').show().text(step === this.totalSteps - 1 ? 'Next →' : 'Next →');
176 $('.wizard-btn-skip').toggle(step > 1);
177 }
178
179 // Scroll to top
180 $('html, body').scrollTop(0);
181
182 // Initialize import button states for step 3
183 if (step === 3) {
184 var self = this;
185 $('.wizard-import-card').each(function () {
186 self.updateImportButtonState($(this));
187 });
188 }
189 },
190
191 /**
192 * Validate current step
193 */
194 validateCurrentStep: function () {
195 // All steps are optional, so always return true
196 // This allows users to skip any step
197 return true;
198 },
199
200 /**
201 * Save step data to server
202 */
203 saveStepData: function () {
204 var stepData = {};
205
206 // Collect data based on current step
207 switch(this.currentStep) {
208
209 case 4: // SEO Settings
210 stepData.seo_settings = {
211 date_archives: $('input[name="seo_date_archives"]').is(':checked'),
212 author_archives: $('input[name="seo_author_archives"]').is(':checked'),
213 category_archives: $('input[name="seo_category_archives"]').is(':checked'),
214 tag_archives: $('input[name="seo_tag_archives"]').is(':checked')
215 };
216 break;
217
218 case 5: // Schema
219 stepData.schema = {
220 enabled: $('#schema-enabled').is(':checked'),
221 default_type: $('input[name="default_schema_type"]:checked').val()
222 };
223 break;
224
225 default:
226 // No data to save for this step
227 return;
228 }
229
230 // Save via AJAX
231 $.post(ajaxurl, {
232 action: 'metasync_save_wizard_progress',
233 nonce: metasyncWizardData.nonce,
234 step: this.currentStep,
235 data: stepData
236 });
237 },
238
239 /**
240 * Trigger SSO authentication
241 */
242 triggerSSO: function () {
243 var self = this;
244 var $button = $('#wizard-connect-btn');
245 var pluginName = metasyncWizardData.pluginName || 'Search Atlas';
246
247 $button.prop('disabled', true).text('Opening SSO...');
248
249 $.post(ajaxurl, {
250 action: 'metasync_generate_connect_url',
251 nonce: metasyncWizardData.saConnectNonce
252 }, function (response) {
253 if (response.success) {
254 // Open SSO popup and store reference
255 self.ssoPopup = window.open(
256 response.data.connect_url,
257 pluginName.replace(/\s+/g, '') + 'SSO',
258 'width=600,height=700'
259 );
260
261 if (self.ssoPopup) {
262 $button.text('Waiting for authentication...');
263 // Poll for completion
264 self.pollSSOStatus(response.data.nonce_token);
265 } else {
266 $button.prop('disabled', false).text('Connect with ' + pluginName);
267 alert('Popup was blocked. Please allow popups for this site and try again.');
268 }
269 } else {
270 $button.prop('disabled', false).text('Connect with ' + pluginName);
271 alert('Failed to generate SSO URL. Please try again.');
272 }
273 }).fail(function () {
274 $button.prop('disabled', false).text('Connect with ' + pluginName);
275 alert('An error occurred. Please try again.');
276 });
277 },
278
279 /**
280 * Poll SSO status
281 */
282 pollSSOStatus: function (nonceToken) {
283 var self = this;
284 var attempts = 0;
285 var maxAttempts = 12; // 60 seconds (12 * 5 seconds)
286 var pluginName = metasyncWizardData.pluginName || 'Search Atlas';
287 var $button = $('#wizard-connect-btn');
288
289 self.ssoPollingInterval = setInterval(function () {
290 attempts++;
291
292 // Update button with countdown
293 var timeLeft = Math.ceil((maxAttempts - attempts) * 5);
294 $button.text('Waiting for authentication (' + timeLeft + 's)...');
295
296 $.post(ajaxurl, {
297 action: 'metasync_check_connect_status',
298 nonce: metasyncWizardData.saConnectNonce,
299 nonce_token: nonceToken
300 }, function (response) {
301 if (response.success && response.data.updated) {
302 // Stop polling and close popup
303 clearInterval(self.ssoPollingInterval);
304 self.ssoPollingInterval = null;
305
306 if (self.ssoPopup && !self.ssoPopup.closed) {
307 self.ssoPopup.close();
308 }
309 self.ssoPopup = null;
310
311 var statusCode = response.data.status_code || 200;
312
313 if (statusCode === 200) {
314 // Success: Update UI to show connected state
315 $('.wizard-step-connection').html(
316 '<div class="wizard-step-header">' +
317 '<h2>🔗 Connect to ' + pluginName + '</h2>' +
318 '<p>Link your WordPress site to your ' + pluginName + ' account for advanced features.</p>' +
319 '</div>' +
320 '<div class="wizard-step-content">' +
321 '<div class="wizard-connection-success">' +
322 '<span class="success-icon">✓</span>' +
323 '<h3>Successfully Connected!</h3>' +
324 '<p>Your site is linked to ' + pluginName + '.</p>' +
325 '</div>' +
326 '</div>'
327 );
328 } else if (statusCode === 403) {
329 // Authentication failed
330 self.resetSSOButton();
331 alert('Authentication failed. Please check your credentials and try again.');
332 } else if (statusCode === 500) {
333 // Server error
334 self.resetSSOButton();
335 alert('Server error occurred. Please try again later or contact support.');
336 } else {
337 // Unknown status
338 self.resetSSOButton();
339 alert('Unexpected response received. Please try again.');
340 }
341 }
342 }).fail(function () {
343 // Continue polling even if individual request fails
344 // Don't show error for temporary network issues
345 console.log('SSO polling request failed, continuing...');
346 });
347
348 // Stop polling after max attempts (timeout)
349 if (attempts >= maxAttempts) {
350 clearInterval(self.ssoPollingInterval);
351 self.ssoPollingInterval = null;
352
353 if (self.ssoPopup && !self.ssoPopup.closed) {
354 self.ssoPopup.close();
355 }
356 self.ssoPopup = null;
357
358 self.resetSSOButton();
359 alert('Authentication timed out after 60 seconds. Please try again and complete the authentication more quickly.');
360 }
361 }, 5000); // Poll every 5 seconds
362 },
363
364 /**
365 * Reset SSO button to initial state
366 */
367 resetSSOButton: function () {
368 var pluginName = metasyncWizardData.pluginName || 'Search Atlas';
369 var $button = $('#wizard-connect-btn');
370
371 $button.prop('disabled', false).text('Connect with ' + pluginName);
372
373 // Clear any active polling
374 if (this.ssoPollingInterval) {
375 clearInterval(this.ssoPollingInterval);
376 this.ssoPollingInterval = null;
377 }
378
379 // Close popup if still open
380 if (this.ssoPopup && !this.ssoPopup.closed) {
381 this.ssoPopup.close();
382 }
383 this.ssoPopup = null;
384 },
385
386 /**
387 * Update import button state based on checkbox selection
388 */
389 updateImportButtonState: function ($card) {
390 var $button = $card.find('.wizard-import-btn');
391 var hasChecked = $card.find('.import-option:checked').length > 0;
392 $button.prop('disabled', !hasChecked);
393 },
394
395 /**
396 * Run import from selected plugin
397 */
398 runImport: function (plugin, $button) {
399 var $card = $button.closest('.wizard-import-card');
400 var $progress = $card.find('.import-progress');
401 var $progressBar = $card.find('.import-progress-bar');
402
403 // Get selected import types
404 var selectedTypes = [];
405 $card.find('.import-option:checked').each(function () {
406 selectedTypes.push($(this).data('type'));
407 });
408
409 if (selectedTypes.length === 0) {
410 alert('Please select at least one import type');
411 return;
412 }
413
414 $button.prop('disabled', true).text('Importing...');
415 $progress.show();
416
417 var totalImports = selectedTypes.length;
418 var completedImports = 0;
419
420 // Import each type sequentially
421 function importNext(index) {
422 if (index >= selectedTypes.length) {
423 // All imports complete
424 $button.text('✓ Import Complete').addClass('success');
425 setTimeout(function () {
426 $progress.fadeOut();
427 }, 1000);
428 return;
429 }
430
431 var type = selectedTypes[index];
432
433 $.post(ajaxurl, {
434 action: 'metasync_import_external_data',
435 nonce: metasyncWizardData.importNonce,
436 type: type,
437 plugin: plugin
438 }, function (response) {
439 completedImports++;
440 var progress = (completedImports / totalImports) * 100;
441 $progressBar.css('width', progress + '%');
442
443 // Import next type
444 importNext(index + 1);
445 }).fail(function () {
446 $button.prop('disabled', false).text('Import Failed');
447 $progress.hide();
448 alert('Import failed for ' + type + '. Please try again.');
449 });
450 }
451
452 importNext(0);
453 },
454
455 /**
456 * Complete wizard
457 */
458 completeWizard: function () {
459 var $button = $('.wizard-complete-btn');
460
461 $button.prop('disabled', true).text('Completing setup...');
462
463 $.post(ajaxurl, {
464 action: 'metasync_complete_wizard',
465 nonce: metasyncWizardData.nonce
466 }, function (response) {
467 if (response.success) {
468 // Redirect to dashboard
469 window.location.href = metasyncWizardData.dashboardUrl;
470 } else {
471 $button.prop('disabled', false).text('Get Started with MetaSync');
472 alert('Failed to complete wizard. Please try again.');
473 }
474 }).fail(function () {
475 $button.prop('disabled', false).text('Get Started with MetaSync');
476 alert('An error occurred. Please try again.');
477 });
478 },
479
480 /**
481 * Check if step is accessible
482 */
483 isStepAccessible: function (step) {
484 // Allow jumping to any previous step or current step
485 return step <= this.currentStep;
486 }
487 };
488
489 // Initialize wizard on page load
490 $(document).ready(function () {
491 if ($('.metasync-wizard-wrap').length) {
492 MetasyncWizard.init();
493 }
494 });
495
496 })(jQuery);
497