# bbp-core/2.4.0/assets/admin/js/setup-wizard.js

Forumax – AI Powered Advanced Community Forum Plugin, version 2.4.0. 314 lines.

- Page: https://pluginprobe.com/plugins/bbp-core/2.4.0/code/assets/admin/js/setup-wizard.js
- Raw: https://pluginprobe.com/plugins/bbp-core/2.4.0/raw/assets/admin/js/setup-wizard.js
- Modified: 2026-04-10T15:32:42+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/bbp-core/2.4.0/code/assets/admin/js/setup-wizard.js#L10-L20`.

```javascript
/**
 * Forumax Setup Wizard JavaScript
 *
 * Single-page multi-step wizard — no page reloads.
 * Updated to work with the Jobus-inspired redesign.
 *
 * @package Forumax
 * @since   2.2.0
 */

(function ($) {
    'use strict';

    const Wizard = {

        currentStep: 1,
        totalSteps:  0,

        init: function () {
            this.totalSteps = parseInt( $('.forumax-setup-card').data('total-steps'), 10 ) || 5;
            this.bindEvents();

            // Restore the last active step from localStorage (defaults to 1).
            const savedStep = parseInt( localStorage.getItem( 'forumax_setup_current_step' ), 10 ) || 1;
            const startStep = ( savedStep >= 1 && savedStep <= this.totalSteps ) ? savedStep : 1;
            this.goToStep( startStep, false );
        },

        bindEvents: function () {
            const self = this;

            // Next / Continue — intercepts the save step to fire AJAX first.
            $(document).on( 'click', '.forumax-btn-next', function () {
                if ( ! self.validateStep( self.currentStep ) ) return;

                const isSaveStep = ( self.currentStep === self.totalSteps - 1 );
                if ( isSaveStep ) {
                    self.saveSettings();
                } else {
                    self.goToStep( self.currentStep + 1 );
                }
            });

            // Previous
            $(document).on( 'click', '.forumax-btn-prev', function () {
                self.goToStep( self.currentStep - 1 );
            });

            // Step indicator — click any step to jump directly to it.
            $(document).on( 'click', '.forumax-progress-step', function () {
                const target = parseInt( $(this).data('step'), 10 );
                if ( target && target !== self.currentStep ) {
                    self.goToStep( target );
                }
            });

            // Radio card / layout option selection
            $(document).on( 'click', '.forumax-option-card, .forumax-layout-option', function () {
                $(this).find('input[type="radio"]').prop('checked', true).trigger('change');
            });

            // Toggle row: sync toggle switch appearance when clicking the row
            $(document).on( 'change', '.forumax-toggle-row input[type="checkbox"]', function () {
                // Visual state handled entirely via CSS :checked sibling selector.
            });

            // Brand color picker ↔ hex input sync.
            $(document).on( 'input', '#brand_color', function () {
                $('#brand_color_hex').val( $(this).val() );
            });
            $(document).on( 'input change', '#brand_color_hex', function () {
                const hex = $(this).val();
                if ( /^#[0-9A-Fa-f]{6}$/.test( hex ) ) {
                    $('#brand_color').val( hex );
                }
            });

            // Auto-save form fields to localStorage
            $(document).on( 'change blur', '.forumax-setup-form input, .forumax-setup-form textarea', function () {
                const name = $(this).attr('name');
                if ( ! name ) return;
                const val = $(this).is(':checkbox') ? $(this).is(':checked') : $(this).val();
                localStorage.setItem( 'forumax_setup_' + name, val );
            });

            this.loadSavedData();
        },

        /**
         * Transition to a specific step.
         *
         * @param {number}  step    Target step number.
         * @param {boolean} animate Whether to animate the transition.
         */
        goToStep: function ( step, animate ) {
            if ( step < 1 || step > this.totalSteps ) return;

            const prev = this.currentStep;
            this.currentStep = step;

            const $steps   = $('.forumax-setup-step');
            const $current = $steps.filter('[data-step="' + prev + '"]');
            const $next    = $steps.filter('[data-step="' + step + '"]');

            if ( animate === false ) {
                $steps.hide().removeClass('forumax-step-animating');
                $next.show();
            } else {
                $current.animate({ opacity: 0 }, 120, function () {
                    $(this).hide().css({ opacity: '' });
                    $next.show().addClass('forumax-step-animating');
                    
                    setTimeout(function() {
                        $next.removeClass('forumax-step-animating');
                    }, 350);
                });
            }

            this.updateProgress( step );
            this.updateNavigation( step );

            // Persist active step so page refresh keeps the user on the same step.
            localStorage.setItem( 'forumax_setup_current_step', step );

            // Scroll card body back to top on step change
            $('.forumax-setup-card-body').scrollTop(0);
        },

        /**
         * Update numbered step circles.
         *
         * @param {number} step Active step number.
         */
        updateProgress: function ( step ) {
            $('.forumax-progress-step').each(function () {
                const n = parseInt( $(this).data('step'), 10 );
                $(this).removeClass('active completed');
                if ( n === step )    $(this).addClass('active');
                else if ( n < step ) $(this).addClass('completed');
            });
        },

        /**
         * Show/hide navigation buttons based on current step.
         *
         * @param {number} step Active step number.
         */
        updateNavigation: function ( step ) {
            const isFirst    = step === 1;
            const isLast     = step === this.totalSteps;
            const isSaveStep = step === this.totalSteps - 1;

            // Hide Back button on first step AND last step (completion)
            $('.forumax-btn-prev').toggle( !isFirst && !isLast );
            
            // Next button hidden on last step
            $('.forumax-btn-next').toggle( !isLast );
            
            // Finish button only shown on last step
            $('.forumax-btn-finish').toggle( isLast );

            // Hide "Skip Setup" link on the last (completion) step
            $('.forumax-btn-skip-wizard').toggle( !isLast );

            // Only show the upgrade banner on the completion step, avoid distraction during setup
            $('.forumax-upgrade-banner').toggle( isLast );

            // Swap label & icon on the last settings step.
            const $next = $('.forumax-btn-next');
            if ( isSaveStep ) {
                $next.html(
                    '<span class="dashicons dashicons-yes-alt"></span> ' +
                    ( forumaxWizard.saveFinish || 'Save & Finish' )
                );
            } else {
                $next.html(
                    ( forumaxWizard.continue || 'Continue' ) +
                    ' <span class="dashicons dashicons-arrow-right-alt2"></span>'
                );
            }
        },

        /**
         * Validate required fields on the current step panel.
         *
         * @param {number} step Step number to validate.
         * @return {boolean} Whether all required fields pass.
         */
        validateStep: function ( step ) {
            const $panel    = $('.forumax-setup-step[data-step="' + step + '"]');
            const $required = $panel.find('input[required], textarea[required]');
            let valid       = true;

            $required.each(function () {
                if ( ! $(this).val() ) {
                    valid = false;
                    $(this).addClass('forumax-field-error');
                    if ( ! $(this).next('.forumax-error-msg').length ) {
                        $('<p class="forumax-error-msg">' + ( forumaxWizard.fieldRequired || 'This field is required' ) + '</p>')
                            .insertAfter(this);
                    }
                } else {
                    $(this).removeClass('forumax-field-error')
                           .next('.forumax-error-msg').remove();
                }
            });

            return valid;
        },

        /**
         * Restore previously saved field values from localStorage.
         */
        loadSavedData: function () {
            $('.forumax-setup-form input, .forumax-setup-form textarea').each(function () {
                const name  = $(this).attr('name');
                if ( ! name ) return;
                const saved = localStorage.getItem( 'forumax_setup_' + name );
                if ( saved === null ) return;

                if ( $(this).is(':checkbox') ) {
                    $(this).prop('checked', saved === 'true');
                } else if ( $(this).is(':radio') ) {
                    if ( $(this).val() === saved ) $(this).prop('checked', true);
                } else {
                    $(this).val(saved);
                }
            });
        },

        /**
         * Collect all wizard form values and POST them to the backend via AJAX.
         * On success, advance to the final completion step.
         */
        saveSettings: function () {
            const self    = this;
            const $btn    = $('.forumax-btn-next');
            const $notice = $('.forumax-save-error');

            // Gather every named input in the wizard.
            const data = { action: 'forumax_save_wizard_settings', nonce: forumaxWizard.nonce };

            $('.forumax-setup-step').find('input, textarea, select').each(function () {
                const name = $(this).attr('name');
                if ( ! name ) return;
                if ( $(this).is(':checkbox') )   data[ name ] = $(this).is(':checked') ? '1' : '0';
                else if ( $(this).is(':radio') ) { if ( $(this).is(':checked') ) data[ name ] = $(this).val(); }
                else                              data[ name ] = $(this).val();
            });

            // Show saving state.
            $btn.prop('disabled', true)
                .html('<span class="dashicons dashicons-update-alt" style="animation:spin 1s linear infinite"></span> ' + ( forumaxWizard.saving || 'Saving…' ) );

            $notice.remove();

            $.post( forumaxWizard.ajaxurl, data )
                .done(function ( res ) {
                    if ( res.success ) {
                        // Clear all localStorage wizard data — DB is now the source of truth.
                        self.clearLocalStorage();
                        self.goToStep( self.totalSteps );
                    } else {
                        self.showSaveError( res.data && res.data.message ? res.data.message : ( forumaxWizard.saveError || 'Could not save settings. Please try again.' ) );
                        $btn.prop('disabled', false);
                        self.updateNavigation( self.currentStep );
                    }
                })
                .fail(function () {
                    self.showSaveError( forumaxWizard.saveError || 'Could not save settings. Please try again.' );
                    $btn.prop('disabled', false);
                    self.updateNavigation( self.currentStep );
                });
        },

        /**
         * Display a save-error notice below the navigation footer.
         *
         * @param {string} message Error text to show.
         */
        showSaveError: function ( message ) {
            $('.forumax-setup-card-footer').after(
                '<p class="forumax-save-error" style="color:#ef4444;font-size:13px;text-align:right;margin:4px 48px 0;">' +
                message + '</p>'
            );
        },
        /**
         * Clear all forumax_setup_* keys from localStorage.
         * Called after a successful AJAX save so stale data cannot overwrite DB values.
         */
        clearLocalStorage: function () {
            for ( let i = localStorage.length - 1; i >= 0; i-- ) {
                const key = localStorage.key(i);
                if ( key && key.startsWith('forumax_setup_') ) {
                    localStorage.removeItem(key);
                }
            }
        },
    };

    $(document).ready(function () {

        if ( $('.forumax-setup-card').length ) {
            Wizard.init();
        }
    });

    // Clear ALL saved wizard data (step + field values) when the wizard is finished.
    $(document).on('click', '.forumax-btn-finish', function () {
        Wizard.clearLocalStorage();
    });

})(jQuery);

```
