Plugin Directory

Changeset 3307540


Ignore:
Timestamp:
06/06/2025 01:22:10 PM (9 months ago)
Author:
aiapponsite
Message:

Release version 1.2.3 with improved theme compatibility, enhanced UI, better error handling, and multiple app configurations

Location:
ai-app-onsite
Files:
124 added
30 edited

Legend:

Unmodified
Added
Removed
  • ai-app-onsite/trunk/ai-app-onsite.php

    r3281194 r3307540  
    44 * Plugin Name: AI App Onsite
    55 * Description: AIappOnsite - AI Web App Creator WP Plugin allows users to create their own AI-powered web app and launch it on their own site with no additional development needed. AI App Onsite web app creator is an AI web app builder for WordPress.
    6  * Version: 1.2.2
     6 * Version: 1.2.3
    77 * Author: By AIappOnsite
    88 * Author URI: https://aiapponsite.com/
     
    1212 */
    1313
     14
     15
    1416// Enqueue WordPress's jQuery
    1517function ai_app_onsite_enqueue_scripts()
     
    1921add_action('wp_enqueue_scripts', 'ai_app_onsite_enqueue_scripts');
    2022
     23if (version_compare(PHP_VERSION, '7.0', '<')) {
     24    add_action('admin_notices', function () {
     25        echo '<div class="notice notice-error"><p><strong>AI App Onsite Plugin:</strong> This plugin requires PHP 7.0 or higher. Your server is running PHP ' . PHP_VERSION . '.</p></div>';
     26    });
     27    return;
     28}
     29
     30add_filter('cron_schedules', function ($schedules) {
     31    $schedules['validatelicense'] = [
     32        'interval' => 86400,
     33        'display' => __('Once Everyday'),
     34    ];
     35    $schedules['checkpluginversion'] = [
     36        'interval' => 43200, // 12 hours in seconds
     37        'display' => __('Twice Daily'),
     38    ];
     39    return $schedules;
     40});
    2141
    2242
     
    2646}
    2747
    28 // Define plugin constants
    29 define('AI_APP_ONSITE_VERSION', '1.2.2');
     48define('AI_APP_ONSITE_VERSION', '1.4.3');
    3049define('AI_APP_ONSITE_PLUGIN_DIR', plugin_dir_path(__FILE__));
    3150define('AI_APP_ONSITE_PLUGIN_URL', plugin_dir_url(__FILE__));
    3251
     52
     53if ( ! function_exists( 'ai_app_onsite_get_version_meta' ) ) {
     54    function ai_app_onsite_get_version_meta() {
     55        $response = wp_remote_get( 'https://aiapponsite.com/prod/ai-app-onsite-version.php?nocache=' . time());
     56
     57        if ( is_wp_error( $response ) ) {
     58            error_log( 'Version fetch failed: ' . $response->get_error_message() );
     59            return [
     60                'error'   => 'Request failed',
     61                'message' => $response->get_error_message(),
     62            ];
     63        }
     64
     65        $body = wp_remote_retrieve_body( $response );
     66        $data = json_decode( $body, true );
     67
     68        if ( ! is_array( $data ) ) {
     69            error_log( 'Invalid JSON returned from version.php: ' . $body );
     70            return [
     71                'error'   => 'Invalid response',
     72                'message' => $body,
     73            ];
     74        }
     75
     76        return $data;
     77    }
     78}
     79
     80
     81function ai_app_onsite_is_premium_active() {
     82    global $wpdb;
     83    $table = $wpdb->prefix . 'ai_app_onsite_license_settings';
     84
     85    // Check if 'key_disable_status' column exists
     86    $column_exists = $wpdb->get_var(
     87        $wpdb->prepare(
     88            "SHOW COLUMNS FROM {$table} LIKE %s",
     89            'key_disable_status'
     90        )
     91    );
     92
     93    $columns = 'key_status';
     94    if ( ! empty( $column_exists ) ) {
     95        $columns .= ', key_disable_status';
     96    }
     97
     98    $row = $wpdb->get_row(
     99        "SELECT {$columns} FROM {$table} LIMIT 1",
     100        ARRAY_A
     101    );
     102
     103    if ( empty( $row ) ) {
     104        return false;
     105    }
     106
     107    if (
     108        isset( $row['key_disable_status'] ) && $row['key_disable_status'] === 'false' &&
     109        isset( $row['key_status'] ) && $row['key_status'] === 'ACTIVE' &&
     110        is_dir( AI_APP_ONSITE_PLUGIN_DIR . 'premium' )
     111    ) {
     112        return true;
     113    }
     114
     115    return false;
     116}
     117
     118function ai_app_onsite_is_premium_disabled() {
     119    global $wpdb;
     120    $table = $wpdb->prefix . 'ai_app_onsite_license_settings';
     121
     122    // Check if 'key_disable_status' column exists
     123    $column_exists = $wpdb->get_var(
     124        $wpdb->prepare(
     125            "SHOW COLUMNS FROM {$table} LIKE %s",
     126            'key_disable_status'
     127        )
     128    );
     129
     130    $columns = 'key_status';
     131    if ( ! empty( $column_exists ) ) {
     132        $columns .= ', key_disable_status, license_key';
     133    }
     134
     135    $row = $wpdb->get_row(
     136        "SELECT {$columns} FROM {$table} LIMIT 1",
     137        ARRAY_A
     138    );
     139
     140    if ( empty( $row ) ) {
     141        return false;
     142    }
     143
     144    if (
     145        isset( $row['key_disable_status'] ) && $row['key_disable_status'] === 'true' &&
     146        isset( $row['key_status'] ) && $row['key_status'] === 'ACTIVE' &&
     147        ! empty( $row['license_key'] ) &&
     148        is_dir( AI_APP_ONSITE_PLUGIN_DIR . 'premium' )
     149    ) {
     150        return true;
     151    }
     152
     153    return false;
     154}
     155
     156
     157
     158function ai_app_onsite_include_path(): string {
     159    return plugin_dir_path(__FILE__) . (ai_app_onsite_is_premium_active() ? 'premium/includes/' : 'includes/');
     160}
     161
     162function ai_app_onsite_handler_path(): string {
     163    return plugin_dir_path(__FILE__) . (ai_app_onsite_is_premium_active() ? 'premium/handler/' : 'handler/');
     164}
     165
     166function ai_app_onsite_assets_path(): string {
     167    return plugin_dir_url(__FILE__) . (ai_app_onsite_is_premium_active() ? 'premium/assets/' : 'assets/');
     168}
     169
    33170// Include necessary files
    34171if (! function_exists('ai_app_onsite_init')) {
    35     function ai_app_onsite_init()
    36     {
    37         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-admin.php';
    38         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-plugin-settings.php';
    39         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-app-properties.php';
    40         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-model-settings.php';
    41         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-field-selector.php';
    42         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-prompt-editor.php';
    43         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-app-preview.php';
    44         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-email-settings.php';
    45 
    46 
    47 
    48         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-plugin-settings.php';
    49         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-app-properties.php';
    50         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-model-settings.php';
    51         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-field-selector.php';
    52         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-prompt-editor.php';
    53         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-app-preview.php';
    54         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-openAi-api.php';
    55         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-user-stats.php';
    56         require_once AI_APP_ONSITE_PLUGIN_DIR . 'handler/ai-app-onsite-email-settings.php';
     172    function ai_app_onsite_init(): void {
     173        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-admin.php';
     174        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-plugin-settings.php';
     175        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-app-properties.php';
     176        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-model-settings.php';
     177        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-field-selector.php';
     178        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-prompt-editor.php';
     179        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-app-preview.php';
     180        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-email-settings.php';
     181
     182        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-plugin-settings.php';
     183        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-app-properties.php';
     184        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-model-settings.php';
     185        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-field-selector.php';
     186        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-prompt-editor.php';
     187        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-app-preview.php';
     188        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-app-license.php';
     189        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-openAi-api.php';
     190        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-user-stats.php';
     191        require_once ai_app_onsite_handler_path() . 'ai-app-onsite-email-settings.php';
    57192
    58193        //$ai_app_onsite = new AI_App_Onsite();
     
    63198
    64199if (!function_exists('ai_app_onsite_enqueue_admin_script')) {
    65     function ai_app_onsite_enqueue_admin_script($hook)
    66     {
     200    function ai_app_onsite_enqueue_admin_script($hook): void {
    67201        // Only load on plugin settings page - adjust this to match your plugin's actual settings page slug
    68202        if ($hook !== 'toplevel_page_ai-app-onsite-menu') {
     
    71205
    72206        // Enqueue CSS for admin
    73         wp_enqueue_style('ai-app-onsite-style', AI_APP_ONSITE_PLUGIN_URL . 'assets/css/ai-app-onsite-style.css', array(), time());
    74         wp_enqueue_style('ai-app-onsite-fafa-style', AI_APP_ONSITE_PLUGIN_URL . 'assets/css/fontawesome.min.css', array(), time());
     207        wp_enqueue_style('ai-app-onsite-style', ai_app_onsite_assets_path() . 'css/ai-app-onsite-style.css', array(), time());
     208        wp_enqueue_style('ai-app-onsite-fafa-style', ai_app_onsite_assets_path() . 'css/fontawesome.min.css', array(), time());
    75209
    76210        // Check if Bootstrap is already loaded
     
    96230        // Enqueue Bootstrap if not already loaded
    97231        if (!$is_bootstrap_loaded) {
    98             wp_register_script('bootstrape-poper-script', AI_APP_ONSITE_PLUGIN_URL . 'assets/js/popper.min.js', array('jquery'), time(), true);
     232            wp_register_script('bootstrape-poper-script', ai_app_onsite_assets_path() . 'js/popper.min.js', array('jquery'), time(), true);
    99233            wp_enqueue_script('bootstrape-poper-script');
    100234
    101             wp_register_script('bootstrape-script-script', AI_APP_ONSITE_PLUGIN_URL . 'assets/js/bootstrap.min.js', array('jquery'), time(), true);
     235            wp_register_script('bootstrape-script-script', ai_app_onsite_assets_path() . 'js/bootstrap.min.js', array('jquery'), time(), true);
    102236            wp_enqueue_script('bootstrape-script-script');
    103237
    104             wp_register_style('ai_app_onsite_style_bootstrap', AI_APP_ONSITE_PLUGIN_URL . 'assets/css/bootstrap.min.css', array(), time());
     238            wp_register_style('ai_app_onsite_style_bootstrap',  ai_app_onsite_assets_path() . 'css/bootstrap.min.css', array(), time());
    105239            wp_enqueue_style('ai_app_onsite_style_bootstrap');
    106240        }
     
    123257
    124258        foreach ($scripts as $handle => $filename) {
    125             wp_register_script($handle, AI_APP_ONSITE_PLUGIN_URL . 'assets/js/' . $filename, array('jquery'), time(), true);
     259            wp_register_script($handle, ai_app_onsite_assets_path() . 'js/' . $filename, array('jquery'), time(), true);
    126260            wp_enqueue_script($handle);
    127261        }
    128262
    129          // Pass the fresh installation flag to JavaScript
    130          $is_fresh_install = get_option('ai_app_onsite_fresh_install', false);
     263       
     264
     265        // Pass the fresh installation flag to JavaScript
     266        $is_fresh_install = get_option('ai_app_onsite_fresh_install', false);
     267       
    131268        // Localize the main admin script
    132269        wp_localize_script(
     
    162299    {
    163300        // Enqueue your script with time() for cache-busting
    164         wp_enqueue_script('ai-app-onsite-openai-api-script', AI_APP_ONSITE_PLUGIN_URL . 'assets/js/ai-app-onsite-openai-api.js', array('jquery'), time(), true);
     301        wp_enqueue_script('ai-app-onsite-openai-api-script', ai_app_onsite_assets_path() . 'js/ai-app-onsite-openai-api.js', array('jquery'), time(), true);
    165302
    166303        // Localize the script with the AJAX URL
     
    192329        // Enqueue styles with time() for cache-busting
    193330        // Register and enqueue stylesheets for the admin area
    194         wp_register_style('ai_app_onsite_form_style', AI_APP_ONSITE_PLUGIN_URL . 'assets/css/ai-app-onsite-form-style.css', array(), time());
     331        wp_register_style('ai_app_onsite_form_style', ai_app_onsite_assets_path() . 'css/ai-app-onsite-form-style.css', array(), time());
    195332        wp_enqueue_style('ai_app_onsite_form_style');
    196333
    197         wp_register_style('ai-app-onsite-fafa-style', AI_APP_ONSITE_PLUGIN_URL . 'assets/css/fontawesome.min.css', array(), time());
     334        wp_register_style('ai-app-onsite-fafa-style', ai_app_onsite_assets_path() . 'css/fontawesome.min.css', array(), time());
    198335        wp_enqueue_style('ai-app-onsite-fafa-style');
    199336        // Check if Bootstrap is already loaded
     
    219356        // Enqueue Bootstrap if not already loaded
    220357        if (!$is_bootstrap_loaded) {
    221             wp_register_style('ai_app_onsite_style_bootstrap', AI_APP_ONSITE_PLUGIN_URL . 'assets/css/bootstrap.min.css', array(), time());
     358            wp_register_style('ai_app_onsite_style_bootstrap', ai_app_onsite_assets_path() . 'css/bootstrap.min.css', array(), time());
    222359            wp_enqueue_style('ai_app_onsite_style_bootstrap');
    223360        }
     
    241378
    242379        add_option('ai_app_onsite_fresh_install', true);
     380
     381        /**
     382         * To initializes and stores encryption keys and license-server URL to avoid hard-coding in WordPress options.
     383         *
     384         * - The `ai_app_onsite_encryption_key` is a 256-bit encryption key generated using `random_bytes`.
     385         * - The `ai_app_onsite_encryption_iv` is a 16-byte initialization vector (IV) encoded in Base64.
     386         * - The `ai_app_onsite_api_key` is a Base64-encoded license server URL string.
     387         *
     388         * These keys are securely generated and stored using WordPress's `update_option` function.
     389         *
     390         * Note: This is not obfuscated code. The Base64 encoding of the API key is used for safe storage
     391         * and transmission, and it is not intended to hide or obscure the key. This approach ensures
     392         * compatibility with systems that may require Base64-encoded strings.
     393         */
     394        if (!get_option('ai_app_onsite_encryption_key')) {
     395            update_option('ai_app_onsite_encryption_key', bin2hex(random_bytes(32))); // 256-bit key
     396        }
     397        if (!get_option('ai_app_onsite_encryption_iv')) {
     398            update_option('ai_app_onsite_encryption_iv', base64_encode(random_bytes(16)));
     399        }
     400        if (!get_option('ai_app_onsite_api_key')) {
     401            update_option('ai_app_onsite_api_key', 'aHR0cHM6Ly9iZHl6OHJvM3BjLmV4ZWN1dGUtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tL1BST0QvdmFsaWRhdGVLZXk=');
     402        }
    243403
    244404        require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-db-handler.php';
     
    273433    }
    274434
    275     wp_enqueue_script('plugin-deactivation-intercept', plugins_url('assets/js/ai-app-onsite-reset-localstorage.js', __FILE__), array('jquery'), '1.0', true);
     435    wp_enqueue_script('plugin-deactivation-intercept', ai_app_onsite_assets_path() . 'js/ai-app-onsite-reset-localstorage.js', array('jquery'), '1.0', true);
    276436});
    277 
    278 
    279 
    280437
    281438
     
    285442        global $wpdb;
    286443
    287         require_once AI_APP_ONSITE_PLUGIN_DIR . 'includes/class-ai-app-onsite-db-handler.php';
     444        require_once ai_app_onsite_include_path() . 'class-ai-app-onsite-db-handler.php';
    288445
    289446        // Create an instance of the database handler class and pass $wpdb
     
    292449        // Call the method to drop the table
    293450        $result = $db_handler->ai_app_onsite_drop_table();
     451
     452        delete_option('ai_app_onsite_encryption_key');
     453        delete_option('ai_app_onsite_encryption_iv');
     454        delete_option('ai_app_onsite_api_key');
    294455
    295456        // Log the result
     
    315476
    316477
    317 add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'ai_app_onsite_settings_link');
    318 
    319 function ai_app_onsite_settings_link($links)
     478add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'ai_app_onsite_settings_and_upgrade_links');
     479function ai_app_onsite_settings_and_upgrade_links($links)
    320480{
    321481    $settings_link = '<a href="' . admin_url('admin.php?page=ai-app-onsite-menu') . '">Settings</a>';
    322     array_unshift($links, $settings_link); // Add it at the beginning
     482    $upgrade_link = '<a href="https://aiapponsite.com/pricing" class="open-new-tab" target="_blank" style="color:#d35400;font-weight:600; text-decoration:none; outline:none; box-shadow:none;">UPGRADE  → <span style="background:#d35400;color:#fff;padding:2px 6px;border-radius:8px;font-size:10px;vertical-align:middle;">PLUS</span></a>';
     483
     484
     485    array_unshift($links, $settings_link);
     486    array_push($links, $upgrade_link);
     487
    323488    return $links;
    324489}
     490
     491add_action('admin_init', function () {
     492    $last_checked = (int) get_option('ai_app_license_last_checked');
     493    $now = time();
     494    if (!$last_checked || ($now - $last_checked) > 3600) {
     495        (new AI_APP_ONSITE_App_License_Handler())->ai_app_onsite_app_check_license_cron_callback();
     496        update_option('ai_app_license_last_checked', $now);
     497    }
     498});
     499
     500if (!function_exists('ai_app_onsite_get_app_selector')) {
     501    function ai_app_onsite_get_app_selector($app_id = 1) {
     502        global $wpdb;
     503
     504        $plugin_settings_table = $wpdb->prefix . 'ai_app_onsite_plugin_settings';
     505        $app_properties_table = $wpdb->prefix . 'ai_app_onsite_app_properties';
     506
     507        // Get the current app_selector value
     508        $app_selector = $wpdb->get_var(
     509            $wpdb->prepare(
     510                "SELECT app_selector FROM $plugin_settings_table WHERE app_id = %d",
     511                $app_id
     512            )
     513        );
     514
     515        // Check if app_selector matches app_name in the app_properties table
     516        if ($app_selector) {
     517            $app_id_from_properties = $wpdb->get_var(
     518                $wpdb->prepare(
     519                    "SELECT id FROM $app_properties_table WHERE app_name = %s OR app_name_show = %s",
     520                    $app_selector,
     521                    $app_selector
     522                )
     523            );
     524
     525            // If a match is found, update app_selector to the id
     526            if ($app_id_from_properties) {
     527                $wpdb->update(
     528                    $plugin_settings_table,
     529                    ['app_selector' => $app_id_from_properties],
     530                    ['app_id' => $app_id],
     531                    ['%d'],
     532                    ['%d']
     533                );
     534
     535                $app_selector = $app_id_from_properties;
     536            }
     537        }
     538
     539        return $app_selector ?: null;
     540    }
     541}
  • ai-app-onsite/trunk/assets/css/ai-app-onsite-form-style.css

    r3281194 r3307540  
    201201.ai-app-form-wrap #ai-app-motivational-heading h6 {
    202202  font-weight: 700;
     203  color: #000;
    203204}
    204205
     
    208209  background-color: #f7f7f7;
    209210  padding: 10px;
     211  color: #000;
    210212  box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
    211213  margin-bottom: 25px;
  • ai-app-onsite/trunk/assets/css/ai-app-onsite-style.css

    r3281194 r3307540  
    280280}
    281281
     282.form-wrapper-license {
     283  width: 100%;
     284  margin: auto;
     285}
     286
    282287.form-wrapper select,
    283288input#banned_words {
     
    319324.fs-regular-12 {
    320325  font-size: 12px;
     326  font-weight: 400;
     327  line-height: 16px;
     328}
     329
     330.fs-regular-10 {
     331  font-size: 10px;
    321332  font-weight: 400;
    322333  line-height: 16px;
     
    13521363}
    13531364
     1365.remove-btn:disabled {
     1366  border:none;
     1367}
     1368
    13541369.remove-btn .btn-icon {
    13551370  margin-right: 2px;
     
    16091624  background-size: inherit;
    16101625  background-position: center;
     1626  cursor: pointer;
    16111627}
    16121628
     
    19952011  background-size: inherit;
    19962012  background-position: center;
     2013  cursor: pointer;
    19972014}
    19982015
     
    20822099    background: transparent;
    20832100  }
     2101.ai-app-onsite-wrap .gold-gradient {
     2102    font-size: 14px;
     2103    font-weight: bold;
     2104    background: linear-gradient(90deg, #FFD700, #FFA500, #FF8C00, #FFD700);
     2105    -webkit-background-clip: text;
     2106    -webkit-text-fill-color: transparent;
     2107    background-clip: text;
     2108    color: transparent;
     2109  }
     2110.ai-app-onsite-wrap .gold-gradient::after {
     2111    background: none;
     2112    content: attr(data-heading) / "";
     2113    left: 0;
     2114    top: 0;
     2115    z-index: -1;
     2116    position: absolute;
     2117    text-shadow:
     2118    -1px 0 1px #c6bb9f,
     2119    0 1px 1px #c6bb9f,
     2120    5px 5px 10px rgba(0, 0, 0, 0.4),
     2121    -5px -5px 10px rgba(0, 0, 0, 0.4);
     2122  }
     2123.ai-app-onsite-wrap .app-name {
     2124    background-color: #efefef;
     2125    pointer-events: none;
     2126  }
     2127
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-admin.js

    r3281194 r3307540  
    44
    55// === Save & Restore Tab using Cookie ===
    6 
    76document.addEventListener('DOMContentLoaded', function () {
    87    function getCookie(name) {
     
    2120
    2221    const isFreshInstall = myPluginAjax.isFreshInstall == '1' ? true : false;
    23     console.log('isFreshInstall:', isFreshInstall);
    2422
    2523    if (isFreshInstall) {
     
    3129        handleTabClick(fallbackTabId); // Activate the default tab
    3230    }
     31
     32    document.querySelectorAll('a.open-new-tab').forEach(function (link) {
     33        link.setAttribute('target', '_blank');
     34        link.setAttribute('rel', 'noopener noreferrer');
     35    });
     36
     37    const observer = new MutationObserver(function (mutationsList) {
     38        mutationsList.forEach(function (mutation) {
     39            mutation.addedNodes.forEach(function (node) {
     40                if (node.nodeType === 1) {
     41                    if (node.matches('a.open-new-tab')) {
     42                        node.setAttribute('target', '_blank');
     43                        node.setAttribute('rel', 'noopener noreferrer');
     44                    } else {
     45                        // Also check any child links inside
     46                        node.querySelectorAll('a.open-new-tab').forEach(function (link) {
     47                            link.setAttribute('target', '_blank');
     48                            link.setAttribute('rel', 'noopener noreferrer');
     49                        });
     50                    }
     51                }
     52            });
     53        });
     54    });
     55   
     56    observer.observe(document.body, { childList: true, subtree: true });
    3357});
    3458
     
    3660
    3761function handleTabClick(tabId) {
    38     event.preventDefault();
     62    // event.preventDefault();
    3963
    4064    var tab = document.getElementById(tabId);
     
    124148                    return;
    125149                }
    126                 console.log("image deleted");
    127150            } else {
    128151                // Handle error response
     
    163186
    164187function uploadSiteLogo(file) {
    165     removeLogoBtn.style.display = 'block';
    166188    fileUploadDiv.classList.add('col-md-8');
    167189    fileUploadDiv.classList.remove('col-md-12');
     
    179201        if (xhr.readyState === 4) { // Ensure the request is complete
    180202            if (xhr.status === 200) {
    181                 console.log('Response Text:', xhr.responseText); // Log the raw response text
    182 
    183203                if (xhr.responseText) { // Check if the response text is not empty
    184204                    try {
     
    187207                        if (app_logo) {
    188208                            var previewImage = document.getElementById('previewImage');
    189                             previewImage.style.backgroundImage = `url('${app_logo}')`;
    190                             previewImage.style.backgroundSize = 'contain'; // Optional: Adjust background size
    191                             previewImage.style.backgroundRepeat = 'no-repeat'; // Optional: Prevent repetition
     209                            previewImage.style.backgroundImage    = `url('${app_logo}')`;
     210                            previewImage.style.backgroundSize     = 'contain'; // Optional: Adjust background size
     211                            previewImage.style.backgroundRepeat   = 'no-repeat'; // Optional: Prevent repetition
    192212                            previewImage.style.backgroundPosition = 'center'; // Optional: Center the image
    193                             previewImage.style.display = 'block';
    194                             previewImage.style.width = '350px';
    195                             previewImage.style.height = '160px';
    196                             previewImage.style.position = 'relative';
     213                            previewImage.style.display          = 'block';
     214                            previewImage.style.width            = '350px';
     215                            previewImage.style.height           = '160px';
     216                            previewImage.style.position         = 'relative';
     217                            removeLogoBtn.style.display         = 'block';
    197218                        }
    198219
     
    202223                        var messageContainer = document.getElementById('app-properties-msg-container');
    203224                        if (messageContainer) {
    204                             var successMessage = 'App Logo Upload Successful';
     225                            var successMessage = 'App Image Uploaded Successful';
    205226                            if (response.success) {
    206227                                messageContainer.innerHTML = '<span style="color: green;">' + successMessage + '</span>';
     
    208229                                messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
    209230                            }
    210                             console.log(response.data, 'parseInt');
    211231
    212232                            var previewImage = document.getElementById('previewImage');
    213                             previewImage.src = response.data;
    214                             previewImage.style.display = 'block';
     233                            previewImage.src            = response.data;
     234                            previewImage.style.display  = 'block';
     235                            removeLogoBtn.style.display = 'block';
    215236                        } else {
    216237                            console.error('messageContainer element not found');
     
    313334    // var existingLabel = document.querySelector('.input-group-color .' + e.name);
    314335
    315     // console.log('existingLabel', existingLabel);
    316 
    317336    // existingLabel.remove();
    318337
     
    367386    var existingLabel = document.querySelector('.input-group-color .' + e.name);
    368387
    369     console.log('existingLabel', existingLabel);
    370388
    371389    existingLabel.remove();
     
    415433    var existingLabel = document.querySelector('.input-group-color .' + e.name);
    416434
    417     console.log('existingLabel', existingLabel);
    418435
    419436    existingLabel.remove();
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-app-preview.js

    r3281194 r3307540  
    88var gutenbergBlock = document.getElementById('openAi-app-gutenberg-block');
    99
     10function ai_app_onsite_create_app_preview_form(is_preview_button = false) {
     11    var loader = document.getElementById('loader');
     12
     13    const appCorner = document.getElementById('app-corner').value;
     14    const appHeight = document.getElementById('app-height').value;
     15
     16    var xhr = new XMLHttpRequest();
     17
     18    xhr.onreadystatechange = function () {
     19        if (xhr.readyState === 4) {
     20            if(is_preview_button){
     21                loader.style.display = 'none';
     22            }
     23
     24            if (xhr.status === 200) {
     25                response = JSON.parse(xhr.responseText);
     26
     27                if (response.status === '404' || response.status === '400') {
     28                    formContainer.innerHTML = '<p>' + response.message + '</p>';
     29                    openAiAppShortcode.value = '';
     30                    testAppContainer.style.display = 'none';
     31                    previewActionContainer.style.display = 'none';
     32                    shortcodeBox.style.display = 'none';
     33                    systemSettingsContainer.style.display = 'none';
     34                    return;
     35                } else {
     36                    formContainer.innerHTML = '';
     37                    testAppContainer.style.display = 'block';
     38                    previewActionContainer.style.display = 'block';
     39                    shortcodeBox.style.display = 'flex';
     40                    systemSettingsContainer.style.display = 'block';
     41                }
     42                // Replace form content
     43                formContainer.innerHTML = response?.form_html;
     44
     45                // Set the data recevied from db
     46                ai_app_onsite_set_app_preview_properties_data(response?.appropriates, is_preview_button);
     47
     48                var parser = new DOMParser();
     49                var doc = parser.parseFromString(response?.form_html, 'text/html');
     50
     51                // Find the element with the class
     52                var element = doc.querySelector('.ai-app-preview-form-heading');
     53
     54                var shortCodeGenerated = doc.querySelector('input[name="short_code_generated"]').value;
     55
     56
     57                // Get the text content of the element
     58                var value = element ? element.textContent : null;
     59                var gutenberg_block = value;
     60                if (gutenberg_block) {
     61                    gutenberg_block = value;
     62                } else {
     63                    gutenberg_block = 'AI App Onsite';
     64                }
     65
     66                if (value) {
     67                    value = value.split(' ').map(function (word) {
     68                        return word.charAt(0).toLowerCase() + word.slice(1).toLowerCase();
     69                    }).join('_');
     70                } else {
     71                    value = 'aiapp';
     72                }
     73
     74                openAiAppShortcode.value = `[${shortCodeGenerated}]`;
     75
     76                const parts = shortCodeGenerated.split("_aiao");
     77
     78                gutenbergBlock.value = parts[0] + ' Aiao';
     79                localStorage.setItem('app_Title', parts[0]);
     80                localStorage.setItem('app_Title_gutenberg_block', parts[0]);
     81
     82                testAppContainer.style.display = 'block';
     83                previewActionContainer.style.display = 'block';
     84                shortcodeBox.style.display = 'flex';
     85                systemSettingsContainer.style.display = 'block';
     86                if (formContainer) {
     87                    // Find the submit button inside the form
     88                    var submitButton = formContainer.querySelector('button[type="button"]');
     89                    if (submitButton) {
     90                        // Disable the submit button
     91                        submitButton.disabled = true;
     92                    }
     93                }
     94                var messageContainer = document.getElementById('app-properties-msg-container-preview');
     95                if(is_preview_button){
     96                    messageContainer.innerHTML = '<span style="color: green;">App Preview has been updated successfully.</span>';
     97                }
     98                setTimeout(function () {
     99                    messageContainer.innerHTML = '';
     100                    const appPreviewSettingsFormTracker = trackFormChanges('#app_preview_settings_form');
     101                    window.FormChangeRegistry.register('app_preview_settings', appPreviewSettingsFormTracker, 'app-preview');
     102           
     103                    document.getElementById('save_ai_app_app_properties_data').addEventListener('click', function () {
     104                        appPreviewSettingsFormTracker.reset();
     105                    });
     106                }, 1000);
     107            }
     108        }
     109    };
     110
     111    xhr.open('POST', myPluginAjax.ajaxurl, true);
     112    if(is_preview_button){
     113        loader.style.display = 'flex';
     114    }
     115
     116    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
     117    const postData = new URLSearchParams();
     118
     119    var titleColorRadios = document.querySelectorAll('input[type="radio"][name="title-color"]');
     120    var titleColors = [];
     121
     122    if (titleColorRadios) {
     123        titleColorRadios.forEach(function (titleColorRadio) {
     124            if (titleColorRadio.checked) {
     125                titleColors.push({ "title_color_selected": titleColorRadio.value });
     126            } else {
     127                titleColors.push({ "title_color": titleColorRadio.value });
     128            }
     129        });
     130    }
     131
     132    postData.append('title_color', JSON.stringify(titleColors));
     133
     134
     135    var fontColorRadios = document.querySelectorAll('input[type="radio"][name="font-color"]');
     136    var fontColor = [];
     137
     138    if (fontColorRadios) {
     139        fontColorRadios.forEach(function (fontColorRadio) {
     140            if (fontColorRadio.checked) {
     141                fontColor.push({ "font_color_selected": fontColorRadio.value });
     142            } else {
     143                fontColor.push({ "font_color": fontColorRadio.value });
     144            }
     145        });
     146    }
     147
     148    postData.append('font_color', JSON.stringify(fontColor));
     149
     150    var bgColorRadios = document.querySelectorAll('input[type="radio"][name="bg-color"]');
     151    var bgColor = [];
     152   
     153    if (bgColorRadios) {
     154        bgColorRadios.forEach(function (bgColorRadio) {
     155            if (bgColorRadio.checked) {
     156                bgColor.push({ "bg_color_selected": bgColorRadio.value });
     157            } else {
     158                bgColor.push({ "bg_color": bgColorRadio.value });
     159            }
     160        });
     161    }
     162
     163    postData.append('background_color', JSON.stringify(bgColor));
     164    postData.append('action', 'ai_app_onsite_create_app_preview_form');
     165    postData.append('app_corner', appCorner);
     166    postData.append('app_height', appHeight);
     167    postData.append('is_preview_button', is_preview_button)
     168
     169    xhr.send(postData.toString());
     170}
     171
    10172function ai_app_onsite_set_app_preview_properties_data(response, is_preview) {
    11173    if (!Array.isArray(response) || response.length === 0) {
     
    28190
    29191            radioInputs.forEach(function (radio) {
     192               
    30193                var label = radio.closest('label');
    31194                var colorWithoutHash = radio.value.replace(/^#/, '');
     
    53216                }
    54217            });
    55         }, 100);
     218    }, 100);
    56219
    57220    // Define an array of objects containing the variables and their corresponding radio button sets
     
    93256                            if (radioButton) {
    94257                                radioButton.checked = true;
     258                                radioButton.setAttribute('checked', 'checked');
    95259                            }
    96260                        });
     
    104268                    if (radioButton) {
    105269                        radioButton.checked = true;
     270                        radioButton.setAttribute('checked', 'checked');
    106271                    } else { // If no matching radio button is found, set the variable as the value of an input field
    107272                        var element = document.getElementById(variableSet.id);
     
    156321                            if (radioButton) {
    157322                                radioButton.checked = true;
     323                                radioButton.setAttribute('checked', 'checked');
    158324                            }
    159325                        });
     
    167333                    if (radioButton) {
    168334                        radioButton.checked = true;
     335                        radioButton.setAttribute('checked', 'checked');
    169336                    } else { // If no matching radio button is found, set the variable as the value of an input field
    170337                        var element = document.getElementById(variableSet.id);
     
    217384                            if (radioButton) {
    218385                                radioButton.checked = true;
     386                                radioButton.setAttribute('checked', 'checked');
    219387                            }
    220388                        });
     
    228396                    if (radioButton) {
    229397                        radioButton.checked = true;
     398                         radioButton.setAttribute('checked', 'checked');
    230399                    } else { // If no matching radio button is found, set the variable as the value of an input field
    231400                        var element = document.getElementById(variableSet.id);
     
    244413    document.getElementById("app-corner").value = app_corner || "soft-corner";
    245414    document.getElementById("app-height").value = app_height || "slight-lift";
     415
     416    document.getElementById('app-corner').setAttribute('name', 'app_corner');
     417    document.getElementById('app-height').setAttribute('name', 'app_height');
     418
     419   
    246420}
    247421function getSelectedTitleColor(data) {
     
    268442    }
    269443    return null;
    270 }
    271 
    272 function ai_app_onsite_create_app_preview_form(is_preview_button = false) {
    273     var loader = document.getElementById('loader');
    274 
    275     // const getCheckedValue = (name) => {
    276     //     const inputs = document.querySelectorAll(`input[name="${name}"]:checked`);
    277     //     return inputs.length ? inputs[0].value : '';
    278     // };
    279 
    280     // const titleColor = getCheckedValue('title-color');
    281     // const fontColor = getCheckedValue('font-color');
    282     // const backgroundColor = getCheckedValue('bg-color');
    283     const appCorner = document.getElementById('app-corner').value;
    284     const appHeight = document.getElementById('app-height').value;
    285 
    286     var xhr = new XMLHttpRequest();
    287 
    288     xhr.onreadystatechange = function () {
    289         if (xhr.readyState === 4) {
    290             if(is_preview_button){
    291                 loader.style.display = 'none';
    292             }
    293 
    294             if (xhr.status === 200) {
    295 
    296                 //console.log('xhr.responseText', xhr.responseText);
    297 
    298                 response = JSON.parse(xhr.responseText);
    299 
    300                 if (response.status === '404' || response.status === '400') {
    301                     formContainer.innerHTML = '<p>No form Field Found Please Create Form Field</p>';
    302                     openAiAppShortcode.value = '';
    303                     testAppContainer.style.display = 'none';
    304                     previewActionContainer.style.display = 'none';
    305                     shortcodeBox.style.display = 'none';
    306                     systemSettingsContainer.style.display = 'none';
    307                     return;
    308                 }
    309                 // Replace form content
    310                 formContainer.innerHTML = response?.form_html;
    311 
    312                 // Set the data recevied from db
    313                 ai_app_onsite_set_app_preview_properties_data(response?.appropriates, is_preview_button);
    314 
    315                 var parser = new DOMParser();
    316                 var doc = parser.parseFromString(response?.form_html, 'text/html');
    317 
    318                 // Find the element with the class
    319                 var element = doc.querySelector('.ai-app-preview-form-heading');
    320 
    321                 var shortCodeGenerated = doc.querySelector('input[name="short_code_generated"]').value;
    322 
    323 
    324                 // Get the text content of the element
    325                 var value = element ? element.textContent : null;
    326                 var gutenberg_block = value;
    327                 if (gutenberg_block) {
    328                     gutenberg_block = value;
    329                 } else {
    330                     gutenberg_block = 'AI App Onsite';
    331                 }
    332 
    333 
    334                 if (value) {
    335                     value = value.split(' ').map(function (word) {
    336                         return word.charAt(0).toLowerCase() + word.slice(1).toLowerCase();
    337                     }).join('_');
    338                 } else {
    339                     value = 'aiapp';
    340                 }
    341 
    342                 openAiAppShortcode.value = `[${shortCodeGenerated}]`;
    343 
    344                 const parts = shortCodeGenerated.split("_aiao");
    345 
    346                 gutenbergBlock.value = parts[0] + ' Aiao';
    347                 localStorage.setItem('app_Title', parts[0]);
    348                 localStorage.setItem('app_Title_gutenberg_block', parts[0]);
    349 
    350                 testAppContainer.style.display = 'block';
    351                 previewActionContainer.style.display = 'block';
    352                 shortcodeBox.style.display = 'flex';
    353                 systemSettingsContainer.style.display = 'block';
    354                 if (formContainer) {
    355                     // Find the submit button inside the form
    356                     var submitButton = formContainer.querySelector('button[type="button"]');
    357                     if (submitButton) {
    358                         // Disable the submit button
    359                         submitButton.disabled = true;
    360                     }
    361                 }
    362                 var messageContainer = document.getElementById('app-properties-msg-container-preview');
    363                 if(is_preview_button){
    364                     messageContainer.innerHTML = '<span style="color: green;">App Preview has been updated successfully.</span>';
    365                 }
    366                 setTimeout(function () {
    367                     messageContainer.innerHTML = '';
    368                 }, 1000);
    369             } else {
    370                 // Handle error response
    371                 console.error('Error occurred: ' + xhr.responseText);
    372 
    373                 formContainer.innerHTML = '<p>It looks like some required settings are missing. Please ensure that "App Properties," "Model Settings," and "Form Fields" are configured before previewing the form.</p>';
    374                 openAiAppShortcode.value = '';
    375                 testAppContainer.style.display = 'none';
    376                 previewActionContainer.style.display = 'none';
    377                 shortcodeBox.style.display = 'none';
    378                 systemSettingsContainer.style.display = 'none';
    379                 return;
    380             }
    381         }
    382     };
    383 
    384     xhr.open('POST', myPluginAjax.ajaxurl, true);
    385     if(is_preview_button){
    386         loader.style.display = 'flex';
    387     }
    388 
    389     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    390     const postData = new URLSearchParams();
    391 
    392     var titleColorRadios = document.querySelectorAll('input[type="radio"][name="title-color"]');
    393     var titleColors = [];
    394 
    395     if (titleColorRadios) {
    396         titleColorRadios.forEach(function (titleColorRadio) {
    397             if (titleColorRadio.checked) {
    398                 titleColors.push({ "title_color_selected": titleColorRadio.value });
    399             } else {
    400                 titleColors.push({ "title_color": titleColorRadio.value });
    401             }
    402         });
    403     }
    404 
    405     postData.append('title_color', JSON.stringify(titleColors));
    406 
    407 
    408     var fontColorRadios = document.querySelectorAll('input[type="radio"][name="font-color"]');
    409     var fontColor = [];
    410 
    411     if (fontColorRadios) {
    412         fontColorRadios.forEach(function (fontColorRadio) {
    413             if (fontColorRadio.checked) {
    414                 fontColor.push({ "font_color_selected": fontColorRadio.value });
    415             } else {
    416                 fontColor.push({ "font_color": fontColorRadio.value });
    417             }
    418         });
    419     }
    420 
    421     postData.append('font_color', JSON.stringify(fontColor));
    422 
    423     var bgColorRadios = document.querySelectorAll('input[type="radio"][name="bg-color"]');
    424     var bgColor = [];
    425     bgColor
    426     if (bgColorRadios) {
    427         bgColorRadios.forEach(function (bgColorRadio) {
    428             if (bgColorRadio.checked) {
    429                 bgColor.push({ "bg_color_selected": bgColorRadio.value });
    430             } else {
    431                 bgColor.push({ "bg_color": bgColorRadio.value });
    432             }
    433         });
    434     }
    435 
    436     postData.append('background_color', JSON.stringify(bgColor));
    437 
    438     postData.append('action', 'ai_app_onsite_create_app_preview_form');
    439     // postData.append('title_color', titleColor);
    440     // postData.append('font_color', fontColor);
    441     // postData.append('background_color', backgroundColor);
    442     postData.append('app_corner', appCorner);
    443     postData.append('app_height', appHeight);
    444     postData.append('is_preview_button', is_preview_button)
    445 
    446     xhr.send(postData.toString());
    447444}
    448445
     
    671668    // If any required field is empty, show an alert and prevent form submission
    672669    if (!isValid) {
    673         console.log('Please fill in all required fields.');
    674670        return false; // Form is invalid
    675671    }
     
    730726
    731727});
    732 
    733 
    734 //Info:working code
    735 // jQuery('body').on('click', '.copy-span', function () {
    736 //     alert("waiting");
    737 //     // Select all span elements with the class 'copy-span'
    738 //     const copySpans = document.querySelectorAll('.copy-span');
    739 
    740 //     // Add event listener to each span
    741 //     copySpans.forEach(function (span) {
    742 //         span.addEventListener('click', function () {
    743 //             // Create a temporary input element to copy the text
    744 //             const textToCopy = span.textContent;
    745 
    746 //             // Create a temporary input element
    747 //             const tempInput = document.createElement('input');
    748 //             document.body.appendChild(tempInput);
    749 
    750 //             // Set the value of the input to the text of the span
    751 //             tempInput.value = textToCopy;
    752 
    753 //             // Select the content in the input
    754 //             tempInput.select();
    755 //             tempInput.setSelectionRange(0, 99999); // For mobile devices
    756 
    757 //             // Copy the content to clipboard
    758 //             try {
    759 //                 document.execCommand('copy');
    760 //                 alert('Copied: ' + textToCopy); // Optional: show a confirmation alert
    761 //             } catch (err) {
    762 //                 console.error('Unable to copy text', err);
    763 //             }
    764 
    765 //             // Remove the temporary input element
    766 //             document.body.removeChild(tempInput);
    767 //         });
    768 //     });
    769 // });
    770 
    771 
    772 // jQuery('body').on('click', '.copy-span', function () {
    773 //     const span = this;  // The clicked span element
    774 //     const textToCopy = span.textContent;
    775 
    776 //     // Create a temporary input element to copy the text
    777 //     const tempInput = document.createElement('input');
    778 //     document.body.appendChild(tempInput);
    779 
    780 //     // Set the value of the input to the text of the span
    781 //     tempInput.value = textToCopy;
    782 
    783 //     // Select the content in the input
    784 //     tempInput.select();
    785 //     tempInput.setSelectionRange(0, 99999); // For mobile devices
    786 
    787 //     // Copy the content to clipboard
    788 //     try {
    789 //         document.execCommand('copy');
    790 //         alert('Copied: ' + textToCopy); // Show the confirmation alert immediately
    791 //     } catch (err) {
    792 //         console.error('Unable to copy text', err);
    793 //     }
    794 
    795 //     // Remove the temporary input element
    796 //     document.body.removeChild(tempInput);
    797 // });
    798728
    799729
     
    845775    }, 2000);
    846776});
     777
     778document.addEventListener('change', function (event) {
     779    if (
     780        event.target.matches('input[type="radio"][name="title-color"]') ||
     781        event.target.matches('input[type="radio"][name="font-color"]') ||
     782        event.target.matches('input[type="radio"][name="bg-color"]')
     783    ) {
     784       
     785        // Remove checked attribute from all radios in the same group
     786        const name = event.target.name;
     787        const allRadios = document.querySelectorAll(`input[type="radio"][name="${name}"]`);
     788        allRadios.forEach(function (radio) {
     789            radio.removeAttribute('checked');
     790        });
     791        // Set checked attribute on selected one
     792        event.target.setAttribute('checked', 'checked');
     793    }
     794});
     795
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-app-properties.js

    r3281194 r3307540  
    99                // Check if status is 404 (Table does not exist or no rows found)
    1010                if (response.status === '404') {
    11                     //alert('Error: ' + response.message);
     11                    document.getElementById('app-properties-msg-container').innerHTML = '<p>It looks like some required settings are missing. Please ensure that "App Settings" are configured & an app has been created before adding app details.</p>';
     12                    document.getElementById('app-details-form').style.display = 'none';
    1213                    return;
     14                }else {
     15                    document.getElementById('app-properties-msg-container').innerHTML = '';
     16                    document.getElementById('app-details-form').style.display = 'block';
    1317                }
    1418                // Accessing the first object in the array
    1519                var firstObject = response[0];
    16                 console.log(firstObject, 'firstObject');
    17                 var app_logo = firstObject.appLogoImagePreview;
    18                 console.log(app_logo, 'app_logo');
    1920                var app_logo = firstObject.appLogoImagePreview;
    2021                var fileUploadDiv = document.getElementById('file-upload-div');
    2122                var removeLogoBtn = document.getElementById('remove-logo-btn');
     23                var previewImage = document.getElementById('previewImage');
    2224
    2325                if (app_logo != 'No preview available') {
    24                     var previewImage = document.getElementById('previewImage');
    2526                    previewImage.style.backgroundImage = `url('${app_logo}')`;
    2627                    previewImage.style.backgroundSize = 'contain'; // Optional: Adjust background size
     
    3334                    fileUploadDiv.classList.remove('col-md-12');
    3435                } else {
    35                     var removeLogoBtn = document.getElementById('remove-logo-btn');
     36                    previewImage.style.backgroundImage = ''; // Remove any previous image
     37                    previewImage.style.display = 'none'; // Hide the preview image
    3638                    removeLogoBtn.style.display = 'none';
    3739                    fileUploadDiv.classList.add('col-md-12');
     
    4951                var submit_button_txt = firstObject.submit_button_txt;
    5052
    51                 // var obj = JSON.parse(title_color);
    52                 if (app_name_show === "") {
     53                if (app_name_show === "" || app_name_show == null) {
    5354                    document.getElementById('app-name').value = app_name;
    5455                } else {
     
    6667                alert('Error: ' + xhr.status + ' ' + xhr.statusText);
    6768            }
     69
     70            const appDetailsFormTracker = trackFormChanges('#app-details-form');
     71            window.FormChangeRegistry.register('app_settings', appDetailsFormTracker, 'app-details');
     72
     73            document.getElementById('save_ai_app_app_properties_data').addEventListener('click', function () {
     74                appDetailsFormTracker.reset();
     75            });
    6876        }
    6977    };
     
    102110//Start save app_properties JS
    103111function ai_app_onsite_save_app_properties() {
    104     console.log('Nonce value:', document.getElementById('ai_app_onsite_app_properties_nonce_field').value);
    105     console.log('Form data:', formData);
    106112    // Clear all previous error messages
    107113    document.querySelectorAll('.fields-error').forEach(function (error) {
     
    126132        errors.push('App Disclaimer is required.');
    127133    }
    128     // if (fileUpload === '') {
    129     //     errors.push('File Upload is required.');
    130     // }
    131134
    132135    // If there are errors, display them and return
     
    143146                document.getElementById('app-disclaimer-error').textContent = error;
    144147            }
    145             // if (error === 'File Upload is required.') {
    146             //     document.getElementById('fileError').textContent = error;
    147             // }
    148148        });
    149149        return;
    150150    }
    151151
    152     // Show loader
    153     var loader = document.getElementById('loader');
    154     loader.style.display = 'flex';
    155 
    156     // Create and send AJAX request
     152    ai_app_onsite_get_plugin_settings_from_properties(function(settings) {
     153        const agreeTerms = Array.isArray(settings)
     154            ? settings[0]?.agree_terms === '1'
     155            : settings?.agree_terms === '1';
     156
     157        if (!agreeTerms) {
     158            const modalElement = document.getElementById('terms_conditions_modal');
     159            const modalInstance = new bootstrap.Modal(modalElement);
     160            modalInstance.show();
     161
     162            return;
     163        }
     164
     165
     166        // Show loader
     167        var loader = document.getElementById('loader');
     168        loader.style.display = 'flex';
     169
     170        // Create and send AJAX request
     171        var xhr = new XMLHttpRequest();
     172        xhr.onreadystatechange = function () {
     173            if (xhr.readyState === 4) {
     174                var messageContainer = document.getElementById('app-properties-msg-container');
     175                setTimeout(function () {
     176                    loader.style.display = 'none';
     177
     178                    if (xhr.status === 200) {
     179                        // Handle the response from the server
     180                        var response = JSON.parse(xhr.responseText);
     181
     182                        if (response.success) {
     183                            // Show success message
     184                            messageContainer.innerHTML = '<span style="color: green;"> ' + response.data + '</span>';
     185                        } else {
     186                            // Show error message if request fails
     187                            messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
     188                        }
     189                    } else {
     190                        // Show error message if request fails
     191                        messageContainer.innerHTML = '<span style="color: red;">Error: Failed to save data</span>';
     192                    }
     193                    // Hide message after 3 seconds
     194                    setTimeout(function () {
     195                        messageContainer.innerHTML = '';
     196                    }, 3000);
     197                }, 2000);
     198            }
     199        };
     200
     201        xhr.open('POST', myPluginAjax.ajaxurl, true);
     202
     203        // Create FormData object
     204        var formData = new FormData();
     205
     206        // Gather form data
     207        formData.append('action', 'ai_app_onsite_save_app_properties');
     208        formData.append('app-name', document.getElementById("app-name") ? document.getElementById("app-name").value : '');
     209        formData.append('app-name-show', document.getElementById("app-name-toggle").checked);
     210
     211        // Check if file is selected
     212        const fileInput = document.getElementById('file-upload');
     213        if (fileInput.files.length > 0) {
     214            const file = fileInput.files[0];
     215            formData.append('app-file', file);
     216        }
     217
     218        formData.append('app-description', document.getElementById("app-description") ? document.getElementById("app-description").value : '');
     219        formData.append('app-disclaimer', document.getElementById("app-disclaimer") ? document.getElementById("app-disclaimer").value : '');
     220        formData.append('submit-btn-txt', document.getElementById("submit-btn-txt") ? document.getElementById("submit-btn-txt").value : '');
     221
     222        // Check if radio buttons are selected before accessing their value
     223        //var titleColorRadio = document.querySelector('input[name="title-color"]:checked');
     224
     225        var titleColorRadios = document.querySelectorAll('input[type="radio"][name="title-color"]');
     226        var titleColors = [];
     227
     228        if (titleColorRadios) {
     229            titleColorRadios.forEach(function (titleColorRadio) {
     230                if (titleColorRadio.checked) {
     231                    titleColors.push({"title_color_selected": titleColorRadio.value});
     232                } else {
     233                    titleColors.push({"title_color": titleColorRadio.value});
     234                }
     235            });
     236        }
     237
     238        // Append titleColors as a nested array within formData
     239        formData.append('titleColors', JSON.stringify(titleColors));
     240
     241        //var fontColorRadio = document.querySelector('input[name="font-color"]:checked');
     242        //formData.append('font-color', fontColorRadio ? fontColorRadio.value : '');
     243        var fontColorRadios = document.querySelectorAll('input[type="radio"][name="font-color"]');
     244        var fontColor = [];
     245
     246        if (fontColorRadios) {
     247            fontColorRadios.forEach(function (fontColorRadio) {
     248                if (fontColorRadio.checked) {
     249                    fontColor.push({"font_color_selected": fontColorRadio.value});
     250                } else {
     251                    fontColor.push({"font_color": fontColorRadio.value});
     252                }
     253            });
     254        }
     255
     256        formData.append('fontColor', JSON.stringify(fontColor));
     257
     258        // var bgColorRadio = document.querySelector('input[name="bg-color"]:checked');
     259        // formData.append('bg-color', bgColorRadio ? bgColorRadio.value : '');
     260        var bgColorRadios = document.querySelectorAll('input[type="radio"][name="bg-color"]');
     261        var bgColor = [];
     262        bgColor
     263        if (bgColorRadios) {
     264            bgColorRadios.forEach(function (bgColorRadio) {
     265                if (bgColorRadio.checked) {
     266                    bgColor.push({"bg_color_selected": bgColorRadio.value});
     267                } else {
     268                    bgColor.push({"bg_color": bgColorRadio.value});
     269                }
     270            });
     271        }
     272
     273        formData.append('bgColor', JSON.stringify(bgColor));
     274
     275        // Include nonce field value
     276        formData.append('ai_app_onsite_app_properties_nonce_field', document.getElementById("ai_app_onsite_app_properties_nonce_field").value);
     277
     278        xhr.send(formData);
     279    });
     280}
     281
     282function ai_app_onsite_get_plugin_settings_from_properties(callback) {
    157283    var xhr = new XMLHttpRequest();
     284    xhr.open('POST', myPluginAjax.ajaxurl, true);
     285    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    158286    xhr.onreadystatechange = function () {
    159287        if (xhr.readyState === 4) {
    160             var messageContainer = document.getElementById('app-properties-msg-container');
    161             setTimeout(function () {
    162                 loader.style.display = 'none';
    163 
    164                 if (xhr.status === 200) {
    165                     // Handle the response from the server
    166                     var response = JSON.parse(xhr.responseText);
    167 
    168                     if (response.success) {
    169                         // Show success message
    170                         messageContainer.innerHTML = '<span style="color: green;"> ' + response.data + '</span>';
    171                     } else {
    172                         // Show error message if request fails
    173                         messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
    174                     }
    175                 } else {
    176                     // Show error message if request fails
    177                     messageContainer.innerHTML = '<span style="color: red;">Error: Failed to save data</span>';
    178                 }
    179                 // Hide message after 3 seconds
    180                 setTimeout(function () {
    181                     messageContainer.innerHTML = '';
    182                 }, 3000);
    183             }, 2000);
     288            var response = JSON.parse(xhr.responseText);
     289            if (xhr.status === 200) {
     290                callback(response);
     291            } else {
     292                console.error('Error fetching plugin settings');
     293                callback({ status: xhr.status, message: xhr.statusText });
     294            }
    184295        }
    185296    };
    186 
    187     xhr.open('POST', myPluginAjax.ajaxurl, true);
    188 
    189     // Create FormData object
    190     var formData = new FormData();
    191 
    192     // Gather form data
    193     formData.append('action', 'ai_app_onsite_save_app_properties');
    194     formData.append('app-name', document.getElementById("app-name") ? document.getElementById("app-name").value : '');
    195     formData.append('app-name-show', document.getElementById("app-name-toggle").checked);
    196 
    197     // Check if file is selected
    198     const fileInput = document.getElementById('file-upload');
    199     if (fileInput.files.length > 0) {
    200         const file = fileInput.files[0];
    201         formData.append('app-file', file);
    202     }
    203 
    204     formData.append('app-description', document.getElementById("app-description") ? document.getElementById("app-description").value : '');
    205     formData.append('app-disclaimer', document.getElementById("app-disclaimer") ? document.getElementById("app-disclaimer").value : '');
    206     formData.append('submit-btn-txt', document.getElementById("submit-btn-txt") ? document.getElementById("submit-btn-txt").value : '');
    207 
    208     // Check if radio buttons are selected before accessing their value
    209     //var titleColorRadio = document.querySelector('input[name="title-color"]:checked');
    210 
    211     var titleColorRadios = document.querySelectorAll('input[type="radio"][name="title-color"]');
    212     var titleColors = [];
    213 
    214     if (titleColorRadios) {
    215         titleColorRadios.forEach(function (titleColorRadio) {
    216             if (titleColorRadio.checked) {
    217                 titleColors.push({ "title_color_selected": titleColorRadio.value });
    218             } else {
    219                 titleColors.push({ "title_color": titleColorRadio.value });
    220             }
    221         });
    222     }
    223 
    224     // Append titleColors as a nested array within formData
    225     formData.append('titleColors', JSON.stringify(titleColors));
    226 
    227     //var fontColorRadio = document.querySelector('input[name="font-color"]:checked');
    228     //formData.append('font-color', fontColorRadio ? fontColorRadio.value : '');
    229     var fontColorRadios = document.querySelectorAll('input[type="radio"][name="font-color"]');
    230     var fontColor = [];
    231 
    232     if (fontColorRadios) {
    233         fontColorRadios.forEach(function (fontColorRadio) {
    234             if (fontColorRadio.checked) {
    235                 fontColor.push({ "font_color_selected": fontColorRadio.value });
    236             } else {
    237                 fontColor.push({ "font_color": fontColorRadio.value });
    238             }
    239         });
    240     }
    241 
    242     formData.append('fontColor', JSON.stringify(fontColor));
    243 
    244     // var bgColorRadio = document.querySelector('input[name="bg-color"]:checked');
    245     // formData.append('bg-color', bgColorRadio ? bgColorRadio.value : '');
    246     var bgColorRadios = document.querySelectorAll('input[type="radio"][name="bg-color"]');
    247     var bgColor = [];
    248     bgColor
    249     if (bgColorRadios) {
    250         bgColorRadios.forEach(function (bgColorRadio) {
    251             if (bgColorRadio.checked) {
    252                 bgColor.push({ "bg_color_selected": bgColorRadio.value });
    253             } else {
    254                 bgColor.push({ "bg_color": bgColorRadio.value });
    255             }
    256         });
    257     }
    258 
    259     formData.append('bgColor', JSON.stringify(bgColor));
    260 
    261     // Include nonce field value
    262     formData.append('ai_app_onsite_app_properties_nonce_field', document.getElementById("ai_app_onsite_app_properties_nonce_field").value);
    263 
    264     xhr.send(formData);
    265 }
    266 
    267 
    268 
     297    xhr.send('action=ai_app_onsite_get_plugin_settings');
     298}
    269299
    270300//=========End save app_properties JS======================
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-field-selector.js

    r3281194 r3307540  
    1919        jQuery(".form-group .ui-state-default").each(function (index) {
    2020            var position = jQuery(this).index();
    21             console.log("Position of div " + index + ": " + position);
    2221        });
    2322    }
     
    141140            if (xhr.status === 200) {
    142141                // Handle success response
    143                 console.log(xhr.responseText);
    144142                // remove div html logically
    145143                jQuery(e).closest(".form-group").remove();
     
    201199        // Use 'group' instead of jQuery(".form-group.ui-state-default")
    202200        var firstUniqueId = jQuery(".field-catcher.ui-sortable .form-group.ui-state-default:first").attr('field_unique_id');
    203         console.log(firstUniqueId, 'firstUniqueId');
    204201        // Get form fields
    205202        var fieldCreator = document.getElementById("filed_creator_" + idx);
     
    321318            // Handle the response from the server
    322319            var response = JSON.parse(xhr.responseText);
    323             // console.log(response);
    324320            var response_html_content = response.html_content;
    325321            //  toggleMaxCharVisibility(response_html_content);
     
    336332                console.error('Error occurred: ' + xhr.responseText);
    337333            }
     334
     335            document.getElementById('app_width_figure').setAttribute('name', 'app_width_figure');
     336            jQuery(".field-catcher .form-group").each(function (index) {
     337                jQuery(this).find("select").attr("name", "filed_creator_" + index);
     338            });
     339
     340            const appFieldSelectorSettingsformTracker = trackFormChanges('#myForm');
     341            window.FormChangeRegistry.register('app_field_selector_settings', appFieldSelectorSettingsformTracker, 'field-selector');
     342
     343            document.getElementById('save_ai_app_ai_app_fieldselector_data').addEventListener('click', function () {
     344                const nonceValue = document.getElementById("ai_app_onsite_field_selector_nonce_field").value;
     345
     346                appFieldSelectorSettingsformTracker.reset();
     347
     348                // Restore nonce after reset
     349                document.getElementById("ai_app_onsite_field_selector_nonce_field").value = nonceValue;
     350            });
    338351        }
    339352    };
     
    374387                    maxCharField.style.display = 'block';
    375388                    assignChar.value = '80'; // Assign the value '80' explicitly
    376                     console.log('textbox', maxCharField.value);
    377389                    fieldSample.style.display = 'block';
    378390                    document.querySelector(`.dropdown-options-col_${index}`).style.display = 'none';
     
    381393                    maxCharField.style.display = 'block';
    382394                    assignChar.value = '300'; // Assign the value '300'
    383                     console.log('textarea', maxCharField.value);
    384395                    fieldSample.style.display = 'block';
    385396                    document.querySelector(`.dropdown-options-col_${index}`).style.display = 'none';
     
    387398                    maxCharField.style.display = 'block';
    388399                    assignChar.value = '10'; // Assign the value '10'
    389                     console.log('number', maxCharField.value);
    390400                    fieldSample.style.display = 'block';
    391401                    document.querySelector(`.dropdown-options-col_${index}`).style.display = 'none';
     
    393403                    maxCharField.style.display = 'block';
    394404                    assignChar.value = '50'; // Assign the value '50'
    395                     console.log('email', maxCharField.value);
    396405                    fieldSample.style.display = 'block';
    397406                    document.querySelector(`.dropdown-options-col_${index}`).style.display = 'none';
     
    415424                    fieldSample.style.display = 'none';
    416425                    dropdownColumn.style.display = 'block';
    417                     console.log('dropdown');
    418426                }
    419427            });
     
    433441        maxCharField.style.display = 'block';
    434442        assignChar.value = '80'; // Assign the value '80' explicitly
    435         console.log('textbox', maxCharField.value);
    436443        fieldSample.style.display = 'block';
    437444        dropdownColumn.style.display = 'none';
     
    439446        maxCharField.style.display = 'block';
    440447        assignChar.value = '300'; // Assign the value '300'
    441         console.log('textarea', maxCharField.value);
    442448        fieldSample.style.display = 'block';
    443449        dropdownColumn.style.display = 'none';
     
    445451        maxCharField.style.display = 'block';
    446452        assignChar.value = '10'; // Assign the value '10'
    447         console.log('number', maxCharField.value);
    448453        fieldSample.style.display = 'block';
    449454        dropdownColumn.style.display = 'none';
     
    451456        maxCharField.style.display = 'block';
    452457        assignChar.value = '50'; // Assign the value '50'
    453         console.log('email', maxCharField.value);
    454458        fieldSample.style.display = 'block';
    455459        dropdownColumn.style.display = 'none';
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-model-settings.js

    r3281194 r3307540  
    101101//Start get model settings from db js
    102102function ai_app_onsite_get_model_settings() {
     103    const appModalSettingsformTracker = trackFormChanges('#ai_app_onsite_reset_modal-setting');
     104    appModalSettingsformTracker.reset();
    103105
    104106    var xhr = new XMLHttpRequest();
     
    109111                // Handle success response
    110112                var response = JSON.parse(xhr.responseText);
    111                 console.log(response, 'ai_app_onsite_get_model_settings');
    112113                if (response.status === '404') {
    113114                    var saveKpenapiKeyBtn = document.getElementById('save-openapi-key');
    114115                    var apiDisableBtn = document.getElementById("api-disable-btn");
    115116                    var apiKeyMgtBtn = document.getElementById("api-key-management");
    116 
     117                    var openaiKey         = document.getElementById('openai-key');
     118                    var openaiKeyHidden   = document.getElementById('openai-key-hidden');
     119
     120                    openaiKey.value = '';
     121                    openaiKeyHidden.value = '';
    117122
    118123                    const engineDropdown = document.getElementById('model');
     
    166171
    167172                    return;
     173                } else {
     174                        document.getElementById('model').classList.remove('border-danger');
     175                        document.getElementById('model-help-text').innerHTML = '';
     176                   
     177                        document.getElementById('model-dependent-fields').classList.remove('blurred');
     178                        document.querySelectorAll('.model-dependent').forEach(function (el) {
     179                            el.disabled = false;
     180                        });
     181                   
     182                        document.getElementById("api-key-management").style.display = 'block';
     183                   
     184                        const apiDisableBtn = document.getElementById("api-disable-btn");
     185                        apiDisableBtn.disabled = false;
     186                   
     187                        // You can also re-enable save if needed
     188                        const saveKpenapiKeyBtn = document.getElementById('save-openapi-key');
     189                        saveKpenapiKeyBtn.removeAttribute('disabled'); // or saveKpenapiKeyBtn.disabled = false;
    168190                }
    169 
    170191
    171192
     
    203224                // Check if the value is null or empty
    204225                if (!openai_key) {
    205                     //jQuery('#apiinfoModal').modal('show');
     226                    openaiKey.value = '';
     227                    openaiKeyHidden.value = '';
     228                   
    206229                    const modalElement = document.getElementById('apiinfoModal');
    207230                    const modalInstance = new bootstrap.Modal(modalElement);
     
    238261
    239262            } else {
    240 
    241263                // Handle error response
    242264                alert('Error occurred: ' + xhr.responseText);
    243265            }
     266
     267            document.getElementById('model').setAttribute('name', 'model');
     268
     269            window.FormChangeRegistry.register('app_modal_settings', appModalSettingsformTracker, 'model-settings');
     270
     271            document.getElementById('save_ai_app_modal_settings_data').addEventListener('click', function () {
     272                appModalSettingsformTracker.reset();
     273            });
    244274        }
    245275    };
     
    249279    xhr.send('action=ai_app_onsite_get_model_settings');
    250280}
    251 
    252281
    253282// Function to update the input value
     
    271300    }
    272301}
    273 
    274 
    275 
    276 
    277302
    278303// Function to send the original API key to the server
     
    432457        // Copy the text inside the text field
    433458        if (document.execCommand('copy')) {
    434             console.log('API key copied to clipboard successfully.');
    435459
    436460            // Change the icon color by adding a class
     
    482506}
    483507
    484 
    485508function ai_app_onsite_get_openapi_data() {
    486509    var xhr = new XMLHttpRequest();
     
    500523                var openai_key = firstObject.openai_key;
    501524                var key_status = firstObject.key_status;
    502                 console.log(key_status, 'key_status');
    503525                if (key_status == 'false') {
    504                     console.log('sderfdfgfgfgfgfgf');
    505526                    document.getElementById('key-status').textContent = 'DISABLED';
    506527                    document.getElementById("api-disable-btn").checked = false;
     
    541562    return maskedPart.slice(0, maxLength);
    542563}
    543 
    544564
    545565// Add event listener for input changes
     
    578598
    579599document.getElementById('api-disable-btn').addEventListener('change', function () {
    580     console.log(this.checked);
    581600    if (this.checked == true) {
    582 
    583601        var switchText = document.getElementsByClassName('text-switch')[0];
    584602        switchText.textContent = 'ENABLED';
     
    646664        }
    647665    }
     666
     667    const modalElement = document.getElementById('apiinfoModal');
     668    modalElement.addEventListener('hidden.bs.modal', function () {
     669        // Remove lingering backdrop
     670        document.querySelectorAll('.modal-backdrop').forEach(el => el.remove());
     671     
     672        // Remove modal-open class and inline styles
     673        document.body.classList.remove('modal-open');
     674        document.body.style.overflow = '';
     675        document.body.style.paddingRight = '';
     676    }, { once: true });
    648677});
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-openai-api.js

    r3281194 r3307540  
    6464}
    6565
    66 
    6766function validateForm(aiappForm) {
    6867    // Check if form exists
     
    8685
    8786        if (!errorParagraph) {
    88             console.error('Error paragraph not found for field with ID:', field.id);
     87            // console.error('Error paragraph not found for field with ID:', field.id);
    8988            return; // Exit the loop if error paragraph is not found
    9089        }
     
    115114    // If any required field is empty, show an alert and prevent form submission
    116115    if (!isValid) {
    117         console.log('Please fill in all required fields.');
    118116        return false; // Form is invalid
    119117    }
     
    121119    return formData; // Return form data if form is valid
    122120}
     121
     122document.addEventListener('keydown', function(event) {
     123    if (event.key === 'Enter') {
     124        event.preventDefault();
     125    }
     126});
     127
     128
     129document.querySelectorAll('a.open-new-tab').forEach(function (link) {
     130    link.setAttribute('target', '_blank');
     131    link.setAttribute('rel', 'noopener noreferrer');
     132});
     133
     134const observer = new MutationObserver(function (mutationsList) {
     135    mutationsList.forEach(function (mutation) {
     136        mutation.addedNodes.forEach(function (node) {
     137            if (node.nodeType === 1) {
     138                if (node.matches('a.open-new-tab')) {
     139                    node.setAttribute('target', '_blank');
     140                    node.setAttribute('rel', 'noopener noreferrer');
     141                } else {
     142                    // Also check any child links inside
     143                    node.querySelectorAll('a.open-new-tab').forEach(function (link) {
     144                        link.setAttribute('target', '_blank');
     145                        link.setAttribute('rel', 'noopener noreferrer');
     146                    });
     147                }
     148            }
     149        });
     150    });
     151});
     152
     153observer.observe(document.body, { childList: true, subtree: true });
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-plugin-settings.js

    r3272415 r3307540  
    11// Function to setup terms of service modal
    22function setupTermsOfServiceModal() {
    3   var termsCheckbox       = document.getElementById("terms_conditions");
    4   var acceptButton        = document.getElementById("accept_ai_app_terms_of_service");
    5   var oldTermsCheckbox    = document.getElementById("terms_of_service-old");
    6   var modal               = document.getElementById("terms_conditions_modal");
    7   var closeButton         = modal.querySelector(".btn-close");
    8   var savePluginSetting   = document.getElementById("save_ai_app_plugin_settings_data");
    9   var termsOfServiceError = document.getElementById('terms_of_service-error');
    10 
    11   // Function to enable/disable accept button based on checkbox status
    12   function toggleAcceptButton() {
    13     if (termsCheckbox.checked) {
    14       acceptButton.disabled = false;
    15     } else {
    16       acceptButton.disabled = true;
     3    var termsCheckbox       = document.getElementById("terms_conditions");
     4    var acceptButton        = document.getElementById("accept_ai_app_terms_of_service");
     5    var oldTermsCheckbox    = document.getElementById("terms_of_service-old");
     6    var modal               = document.getElementById("terms_conditions_modal");
     7    var closeButton         = modal.querySelector(".btn-close");
     8    var savePluginSetting   = document.getElementById("save_ai_app_plugin_settings_data");
     9    var termsOfServiceError = document.getElementById('terms_of_service-error');
     10 
     11    // Function to enable/disable accept button based on checkbox status
     12    function toggleAcceptButton() {
     13      if (termsCheckbox.checked) {
     14        acceptButton.disabled = false;
     15      } else {
     16        acceptButton.disabled = true;
     17      }
    1718    }
    18   }
    19 
    20   // Initially check the checkbox status
    21   toggleAcceptButton();
    22 
    23   // Event listener for checkbox change
    24   termsCheckbox.addEventListener("change", function() {
     19 
     20    // Initially check the checkbox status
    2521    toggleAcceptButton();
    26   });
    27 
    28   // Event listener for accept button click
    29   acceptButton.addEventListener("click", function() {
    30     if (termsCheckbox.checked) {
    31       oldTermsCheckbox.checked = true; // Check the other checkbox
    32       termsOfServiceError.textContent = '';
    33       closeModal(); // Close the modal
     22 
     23    // Event listener for checkbox change
     24    termsCheckbox.addEventListener("change", function() {
     25      toggleAcceptButton();
     26    });
     27 
     28    // Event listener for accept button click
     29    acceptButton.addEventListener("click", function() {
     30      if (termsCheckbox.checked) {
     31        oldTermsCheckbox.checked = true; // Check the other checkbox
     32        termsOfServiceError.textContent = '';
     33        closeModal(); // Close the modal
     34      }
     35    });
     36 
     37    // Event listener for close button click
     38    closeButton.addEventListener("click", function() {
     39      if (!termsCheckbox.checked) {
     40        oldTermsCheckbox.checked = false; // Uncheck the other checkbox
     41      }
     42    });
     43 
     44    // Function to close the modal
     45    function closeModal() {
     46      modal.classList.remove("show");
     47      modal.style.display = "none";
     48      document.body.classList.remove("modal-open");
     49      document.body.style.paddingRight = "";
     50      document.querySelector(".modal-backdrop").remove();
    3451    }
    35   });
    36 
    37   // Event listener for close button click
    38   closeButton.addEventListener("click", function() {
    39     if (!termsCheckbox.checked) {
    40       oldTermsCheckbox.checked = false; // Uncheck the other checkbox
     52 
     53    function disable(){
     54      oldTermsCheckbox.addEventListener("change", function() {
     55          if (oldTermsCheckbox.checked) {
     56              savePluginSetting.disabled = false;
     57          } else {
     58            savePluginSetting.disabled = true;   
     59          }
     60      });
    4161    }
    42   });
    43 
    44   // Function to close the modal
    45   function closeModal() {
    46     modal.classList.remove("show");
    47     modal.style.display = "none";
    48     document.body.classList.remove("modal-open");
    49     document.body.style.paddingRight = "";
    50     document.querySelector(".modal-backdrop").remove();
    51   }
    52 
    53   function disable(){
    54     oldTermsCheckbox.addEventListener("change", function() {
    55         if (oldTermsCheckbox.checked) {
    56             savePluginSetting.disabled = false;
    57         } else {
    58           savePluginSetting.disabled = true;   
     62    //disable();
     63  }
     64 
     65  // Call the setup function after the DOM is loaded
     66    setupTermsOfServiceModal();
     67  //================End terms of service modal js =========================
     68 
     69  function ai_app_onsite_show_terms_of_service() {
     70      const termsOfServiceModal = document.getElementById("terms_conditions_modal");
     71      const termsOfServiceCheckbox = document.getElementById("terms-form-group");
     72      const divider = document.getElementById("divider");
     73      const termsOfServiceButtons = document.getElementById("terms_conditions_button");
     74 
     75      const modalInstance = new bootstrap.Modal(termsOfServiceModal);
     76      modalInstance.show();
     77 
     78      termsOfServiceCheckbox.style.display = "none";
     79      divider.style.display = "none";
     80      termsOfServiceButtons.style.display = "none";
     81  }
     82 
     83  let isPluginSaveTriggered = false;
     84  let isSwitchChecked = false;
     85 
     86  //Start save plugin settings JS
     87  function ai_app_onsite_save_plugin_settings() {
     88      // Clear all previous error messages
     89      document.querySelectorAll('.fields-error').forEach(function(error) {
     90          error.textContent = '';
     91      });
     92 
     93      // Get form fields
     94      var terms_of_service = document.getElementById("terms_of_service-old").checked;
     95 
     96      // Collect error messages
     97      var errors = [];
     98      if (!terms_of_service) {
     99          errors.push('Please Agree to Terms of Service.');
     100          const modalElement = document.getElementById('terms_conditions_modal');
     101          const modalInstance = new bootstrap.Modal(modalElement);
     102          modalInstance.show();
     103 
     104          const termsOfServiceCheckbox = document.getElementById("terms-form-group");
     105          const divider = document.getElementById("divider");
     106          const termsOfServiceButtons = document.getElementById("terms_conditions_button");
     107 
     108          termsOfServiceCheckbox.style.display = "block";
     109          divider.style.display = "block";
     110          termsOfServiceButtons.style.display = "flex";
     111          isPluginSaveTriggered = true;
     112      }else {
     113          isPluginSaveTriggered = false;
     114      }
     115     
     116      // If there are errors, display them and return
     117      if (errors.length > 0) {
     118          errors.forEach(function(error) {
     119              if (error === 'Please Agree to Terms of Service.') {
     120                  document.getElementById('terms_of_service-error').textContent = error;
     121              }
     122          });
     123          return;
     124      }
     125    // Show loader
     126    var loader = document.getElementById('loader');
     127    loader.style.display = 'flex';
     128 
     129    var xhr = new XMLHttpRequest();
     130    xhr.onreadystatechange = function() {
     131        if (xhr.readyState === 4) {
     132            // Show success message
     133            var messageContainer = document.getElementById('Plugin-settings-msg-container');
     134            setTimeout(function() {
     135                loader.style.display = 'none';
     136 
     137                if (xhr.status === 200) {
     138                    // Handle the response from the server
     139                    var response = JSON.parse(xhr.responseText);
     140                    if (response.success) {
     141                        messageContainer.innerHTML = '<span style="color: green;"> ' + response.data + '</span>';
     142                        ai_app_onsite_get_plugin_settings();
     143                    }else{
     144                        messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
     145                    }
     146                } else {
     147                    messageContainer.innerHTML = '<span style="color: red;">Error: Failed to save data</span>';
     148                }
     149                // Hide message after 3 seconds
     150                setTimeout(function() {
     151                    messageContainer.innerHTML = '';
     152                }, 3000);
     153            }, 2000);
    59154        }
    60     });
    61   }
    62   //disable();
    63 }
    64 
    65 // Call the setup function after the DOM is loaded
    66   setupTermsOfServiceModal();
    67 //================End terms of service modal js =========================
    68 
    69 //Start save plugin settings JS
    70 function ai_app_onsite_save_plugin_settings() {
    71     // Clear all previous error messages
    72     document.querySelectorAll('.fields-error').forEach(function(error) {
    73         error.textContent = '';
    74     });
    75 
    76     // Get form fields
    77     var terms_of_service = document.getElementById("terms_of_service-old").checked;
    78 
    79     // Collect error messages
    80     var errors = [];
    81     if (!terms_of_service) {
    82         errors.push('Please Agree to Terms of Service.');
    83     }
    84    
    85     // If there are errors, display them and return
    86     if (errors.length > 0) {
    87         errors.forEach(function(error) {
    88             if (error === 'Please Agree to Terms of Service.') {
    89                 document.getElementById('terms_of_service-error').textContent = error;
    90             }
    91         });
    92         return;
    93     }
    94   // Show loader
    95   var loader = document.getElementById('loader');
    96   loader.style.display = 'flex';
    97 
    98   var xhr = new XMLHttpRequest();
    99   xhr.onreadystatechange = function() {
    100       if (xhr.readyState === 4) {
    101           // Show success message
    102           var messageContainer = document.getElementById('Plugin-settings-msg-container');
    103           setTimeout(function() {
    104               loader.style.display = 'none';
    105 
     155    };
     156 
     157    xhr.open('POST', myPluginAjax.ajaxurl, true);
     158 
     159    var formData = new FormData();
     160      formData.append('action', 'ai_app_onsite_save_plugin_settings');
     161      formData.append('terms_of_service', document.getElementById("terms_of_service-old") ? document.getElementById("terms_of_service-old").checked : false);
     162      formData.append('app_selector', document.getElementById("app_selector") ? document.getElementById("app_selector").value : "");
     163      formData.append('app_email', document.getElementById("app_email") ? document.getElementById("app_email").value : "");
     164 
     165      // Gather selected banned words
     166      var bannedWordsSelect = document.querySelector('[data-multi-select-plugin]');
     167      var selectedOptions = bannedWordsSelect.selectedOptions;
     168      var banned_words = [];
     169      let selectedLabels = document.querySelectorAll('.selected-wrapper .selected-label');
     170 
     171        // Loop through each of the selected labels
     172      selectedLabels.forEach(function(label) {
     173          banned_words.push(label.textContent);
     174      });
     175      // Append each banned word to the formData object
     176      for (var j = 0; j < banned_words.length; j++) {
     177          formData.append('banned_words[]', banned_words[j]);
     178      }
     179 
     180 
     181      // Append the CSV file
     182      const fileInput = document.getElementById('csv-upload');
     183      if (fileInput.files.length > 0) {
     184          const file = fileInput.files[0];
     185          formData.append('csv-file', file);
     186      }
     187 
     188      // Append nonce field value
     189      formData.append('ai_app_onsite_plugin_settings_nonce_field', document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value);
     190 
     191         
     192    xhr.send(formData);
     193  }
     194 
     195  //=======================End save plugin settings js===========================
     196 
     197  //Start get plugin setting from db js
     198  function ai_app_onsite_get_plugin_settings() {
     199      var xhr = new XMLHttpRequest();
     200 
     201      xhr.onreadystatechange = function() {
     202          if (xhr.readyState === 4) {
    106203              if (xhr.status === 200) {
    107                   // Handle the response from the server
    108                   var response = JSON.parse(xhr.responseText);
    109                   if (response.success) {
    110                       messageContainer.innerHTML = '<span style="color: green;"> ' + response.data + '</span>';
    111                   }else{
    112                       messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
    113                   }
     204                // Handle success response
     205                var response = JSON.parse(xhr.responseText);
     206                if (response.status === "404") {
     207                    return;
     208                };
     209 
     210                // Accessing the first object in the array
     211                var firstObject = response[0];
     212                 
     213                // Accessing properties of the first object
     214                var id = firstObject.id;
     215                var agreeTerms = firstObject.agree_terms;
     216                var appSelector = firstObject.app_selector;
     217                var userStats = firstObject.user_stats;
     218                var bannedWords = firstObject.banned_words;
     219                var bannedWordsCSV = firstObject.banned_words_csv;
     220                var appName = firstObject.appName;
     221                const selectElement = document.getElementById("bannedWords");
     222               
     223                var app_email = firstObject.app_email;
     224 
     225                // Assume selectElement is already defined as your target select element
     226                bannedWordsCSV.forEach((item) => {
     227                  // Check if an option with the same value already exists
     228                  const exists = Array.from(selectElement.options).some(
     229                    (option) => option.value === item[0]
     230                  );
     231 
     232                  if (!exists) {
     233                    // Create a new option element
     234                    const option = document.createElement("option");
     235 
     236                    // Set the value and text of the option element
     237                    option.value = item[0];
     238                    option.text = item[0];
     239 
     240                    // Append the option element to the select element
     241                    selectElement.appendChild(option);
     242                  }
     243 
     244                  const optionToSelect = Array.from(selectElement.options).find(
     245                    (option) => option.value === item[0]
     246                  );
     247                  if (optionToSelect) {
     248                    optionToSelect.selected = true;
     249                  }
     250                });
     251 
     252                if (
     253                  appName != null &&
     254                  appName !== "" &&
     255                  appName !== "undefined"
     256                ) {
     257                  var selectElements = document.getElementById("app_selector");
     258                  selectElements.value = appName;
     259                }
     260                var app_emails = document.getElementById("app_email");
     261                app_emails.value = app_email;
     262 
     263                // Set checkbox checked if agreeTerms is "1"
     264                if (agreeTerms === "1") {
     265                  document.getElementById("terms_of_service-old").checked = true;
     266                  // Ensure the modal is not triggered
     267                }
     268 
     269                var searchContainer = document.querySelector(".search-container");
     270 
     271                var searchContainer = document.querySelector(".search-container"); // Ensure the selector is correct
     272                if (!searchContainer) {
     273                  console.error("searchContainer element not found!");
     274                  return; // Exit if the container is missing
     275                }
     276                bannedWords.forEach(function (value) {
     277                  if (value) {
     278                    // Ensure value is not empty or falsy
     279                    var exists = document.querySelector(
     280                      '.selected-wrapper span.selected-label[data-value="' +
     281                        value +
     282                        '"]'
     283                    );
     284                    if (!exists) {
     285                      // Create new elements only if the value is not empty and doesn't already exist
     286                      var newDiv = document.createElement("div");
     287                      newDiv.className = "selected-wrapper";
     288 
     289                      var labelSpan = document.createElement("span");
     290                      labelSpan.className = "selected-label";
     291                      labelSpan.textContent = value;
     292                      labelSpan.setAttribute("data-value", value);
     293 
     294                      var closeLink = document.createElement("a");
     295                      closeLink.className = "selected-close";
     296                      closeLink.setAttribute("tabindex", "-1");
     297                      closeLink.setAttribute("data-option", value);
     298                      closeLink.setAttribute("data-hits", "0");
     299                      closeLink.href = "#";
     300                      closeLink.textContent = "x";
     301 
     302                      newDiv.appendChild(labelSpan);
     303                      newDiv.appendChild(closeLink);
     304                      searchContainer.before(newDiv); // Ensure searchContainer exists before this line
     305                    }
     306                  }
     307                });
     308 
     309                document
     310                  .querySelectorAll(".selected-wrapper")
     311                  .forEach((wrapper) => {
     312                    const label = wrapper.querySelector(".selected-label");
     313                    if (!label.hasAttribute("data-value")) {
     314                      wrapper.remove();
     315                    }
     316                  });
     317 
     318                // Event listener for the dropdown icon click
     319                const dropdownIcon = document.querySelector(".dropdown-icon");
     320                if (dropdownIcon) {
     321                  dropdownIcon.addEventListener("click", function () {
     322                    if (this.classList.contains("active")) {
     323                      document
     324                        .querySelectorAll(".selected-wrapper .selected-label")
     325                        .forEach(function (element) {
     326                          var valueToRemove = element.textContent.trim();
     327                          document
     328                            .querySelectorAll(".autocomplete-list li")
     329                            .forEach(function (liElement) {
     330                              if (
     331                                liElement.textContent.trim() === valueToRemove
     332                              ) {
     333                                liElement.parentNode.removeChild(liElement);
     334                              }
     335                            });
     336                        });
     337                    } else {
     338                      console.log("Dropdown icon is not active");
     339                    }
     340                  });
     341                } else {
     342                  console.log("Dropdown icon not found");
     343                }
    114344              } else {
    115                   messageContainer.innerHTML = '<span style="color: red;">Error: Failed to save data</span>';
     345                  // Handle error response
     346                  alert('Error occurred: ' + xhr.responseText);
     347              }
     348 
     349              const appSettingsformTracker = trackFormChanges('#app_settings_form');
     350              window.FormChangeRegistry.register('app_settings', appSettingsformTracker, 'app-settings');
     351 
     352              document.getElementById('save_ai_app_plugin_settings_data').addEventListener('click', function () {
     353                  appSettingsformTracker.reset();
     354              });
     355 
     356             
     357          }
     358      };
     359 
     360      xhr.open('POST', myPluginAjax.ajaxurl, true);
     361      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
     362      xhr.send('action=ai_app_onsite_get_plugin_settings');
     363 
     364      ai_app_onsite_get_app_license_key();
     365  }
     366 
     367  /*=============End get plugin setting from db js============================== */
     368 
     369 
     370  /*=============Download terms Of service js=================================== */
     371 
     372  function ai_app_onsite_download_terms_Of_service() {
     373      const { jsPDF } = window.jspdf;
     374      const doc = new jsPDF();
     375      const termsContent = document.getElementById('terms-conditions-modal-pdf').innerText;
     376 
     377      const maxWidth = 180; // Maximum width for text in mm
     378          const startX = 10; // X-coordinate to start text
     379          const startY = 10; // Y-coordinate to start text
     380          const lineHeight = 4.5; // Approximate line height in mm
     381          const pageHeight = 297; // A4 page height in mm (210x297mm)
     382          const marginBottom = 10; // Bottom margin
     383 
     384          let cursorY = startY; // Track current Y-coordinate
     385 
     386          // Set font size
     387          const fontSize = 12; // Set the font size as desired
     388          doc.setFontSize(fontSize);
     389 
     390          // Split text into lines that fit within the specified max width
     391          const lines = doc.splitTextToSize(termsContent, maxWidth);
     392 
     393          // Add the lines of text to the PDF
     394          lines.forEach(line => {
     395              if (cursorY + lineHeight > pageHeight - marginBottom) {
     396                  // Add new page if the text exceeds the page height
     397                  doc.addPage();
     398                  cursorY = startY; // Reset cursorY for new page
     399              }
     400              doc.text(line, startX, cursorY);
     401              cursorY += lineHeight * (fontSize / 10); // Adjust cursorY based on font size
     402          });
     403      doc.save('AIappOniste-terms-of-service.pdf');
     404  }
     405 
     406  /*=============End download terms Of service js=================================== */
     407 
     408 
     409  /*=============Download user stats js=================================== */
     410  function ai_app_onsite_download_user_stats(event) {
     411      event.preventDefault();
     412      // Show loader
     413      document.getElementById('loader').style.display = 'flex';
     414      var messageContainer = document.getElementById('Plugin-settings-msg-container');
     415 
     416      // Get the selected month (e.g., from a dropdown or an input)
     417      const month = document.getElementById('ai_app_onsite-user-stats').value;
     418      var userStatsError = document.getElementById('ai_app_onsite-user-stats-error');
     419      userStatsError.textContent = '';
     420      var ai_app_onsite_download_user_stats_nonce = myPluginAjax.downloadUserStatsNonce;
     421     
     422      // Prepare data to send
     423     const data = new FormData();
     424      data.append('ai_app_onsite_download_user_stats_nonce', ai_app_onsite_download_user_stats_nonce); // Correctly use the nonce variable
     425      data.append('action', 'ai_app_onsite_download_user_stats'); // Action name
     426      data.append('month', month); // Pass the month value
     427      // Make AJAX request
     428      fetch(ajaxurl, {
     429          method: 'POST',
     430          body: data,
     431      })
     432      .then(response => {
     433          if (!response.ok) {
     434              throw new Error('Network response was not ok');
     435          }
     436          return response.text(); // Read response as text
     437      })
     438      .then(text => {
     439          // Hide loader after a delay (e.g., 1 second)
     440          setTimeout(() => {
     441              document.getElementById('loader').style.display = 'none';
     442 
     443              // Check if the response indicates failure and no data found
     444              if (text.includes('No user statistics found.')) {
     445                  // Show error message
     446                  userStatsError.textContent = 'No user statistics found.';
     447              } else {
     448                  // Create a blob from the response text
     449                  const blob = new Blob([text], { type: 'text/csv' });
     450 
     451                  // Create a link element
     452                  const url = window.URL.createObjectURL(blob);
     453                  const a = document.createElement('a');
     454                  a.style.display = 'none';
     455                  a.href = url;
     456                  a.download = 'AIappOniste_User_Stats.csv';
     457 
     458                  // Append the link to the body
     459                  document.body.appendChild(a);
     460                  a.click();
     461 
     462                  // Remove the link
     463                  document.body.removeChild(a);
     464 
     465                  // Revoke the object URL
     466                  window.URL.revokeObjectURL(url);
     467 
     468                  messageContainer.innerHTML = '<span style="color: green;">User stats downloaded successfully</span>';
     469 
     470              }
     471              setTimeout(function() {
     472                  messageContainer.innerHTML = '';
     473              }, 3000);
     474          }, 2000); // Adjust delay time as needed
     475      })
     476      .catch(error => {
     477          // Hide loader
     478          document.getElementById('loader').style.display = 'none';
     479 
     480          // Show error message
     481          document.getElementById('ai_app_onsite-user-stats-error').textContent = 'Error: ' + error.message;
     482      });
     483  }
     484 
     485  //upload file js
     486  function validateCSVFile(input) {
     487      var file = document.getElementById('csv-upload').files[0];
     488      var fileError = document.getElementById('fileError');
     489      // Check if file is selected
     490      if (!file) {
     491          fileError.textContent = 'Please select a file.';
     492         return;
     493      }
     494 
     495      // Check file type
     496      var validExtensions = ['text/csv'];
     497      if (!validExtensions.includes(file.type)) {
     498          fileError.textContent = 'Please select a valid image file (SVG, PNG, JPG, or GIF).';
     499          this.value = ''; // Clear the input field
     500       
     501          return;
     502      }
     503 
     504      fileError.textContent = ''; // Clear any previous errors
     505 
     506         uploadCSVFile(file);
     507           // Reset input value to allow same file to be selected again
     508      input.value = '';
     509  }
     510 
     511 
     512  function uploadCSVFile(file) {
     513      var formData = new FormData();
     514      formData.append('csv-file', file);
     515      formData.append('action', 'ai_app_onsite_handle_csv_upload');
     516      formData.append('ai_app_onsite_plugin_settings_nonce_csv_upload_field', document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value);
     517 
     518      var xhr = new XMLHttpRequest();
     519      xhr.open('POST', myPluginAjax.ajaxurl, true);
     520     
     521      xhr.onreadystatechange = function () {
     522          if (xhr.readyState === 4) { // Ensure the request is complete
     523              if (xhr.status === 200) {
     524 
     525                  if (xhr.responseText) { // Check if the response text is not empty
     526                      try {
     527                          var response = JSON.parse(xhr.responseText);
     528                          var messageContainer = document.getElementById('Plugin-settings-msg-container');
     529                          if (messageContainer) {
     530                              if (response.success) {
     531                                  messageContainer.innerHTML = '<span style="color: green;">' + response.data + '</span>';
     532                              } else {
     533                                  messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
     534                              }
     535                               ai_app_onsite_get_plugin_settings();   
     536 
     537                          } else {
     538                              console.error('messageContainer element not found');
     539                          }
     540                      } catch (e) {
     541                          console.error('Failed to parse response:', e);
     542                          var messageContainer = document.getElementById('messageContainer');
     543                          if (messageContainer) {
     544                              messageContainer.innerHTML = '<span style="color: red;">Error parsing response</span>';
     545                          } else {
     546                              console.error('messageContainer element not found');
     547                          }
     548                      }
     549                  } else {
     550                      console.error('Empty response');
     551                      var messageContainer = document.getElementById('messageContainer');
     552                      if (messageContainer) {
     553                          messageContainer.innerHTML = '<span style="color: red;">Empty response</span>';
     554                      } else {
     555                          console.error('messageContainer element not found');
     556                      }
     557                  }
     558              } else {
     559                  console.error('Request failed with status:', xhr.status);
     560                  var messageContainer = document.getElementById('messageContainer');
     561                  if (messageContainer) {
     562                      messageContainer.innerHTML = '<span style="color: red;">Error: ' + xhr.status + '</span>';
     563                  } else {
     564                      console.error('messageContainer element not found');
     565                  }
    116566              }
    117567              // Hide message after 3 seconds
     
    119569                  messageContainer.innerHTML = '';
    120570              }, 3000);
    121           }, 2000);
     571 
     572 
     573          }
     574      };
     575 
     576      xhr.send(formData);
     577  }
     578 
     579  function uploadTermCondition(checkbox) {
     580      var terms_of_service = document.getElementById("terms_of_service-old").checked;
     581      var terms_of_service_nonce = document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value;
     582      var xhr = new XMLHttpRequest(); // Initialize the XMLHttpRequest object
     583      xhr.open('POST', myPluginAjax.ajaxurl, true);
     584      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Set the request header
     585 
     586      xhr.onreadystatechange = function () {
     587          if (xhr.readyState === 4) { // Ensure the request is complete
     588              var messageContainer = document.getElementById('Plugin-settings-msg-container') || document.getElementById('messageContainer');
     589              if (xhr.status === 200) {
     590 
     591                  if (xhr.responseText) { // Check if the response text is not empty
     592                      try {
     593                          var response = JSON.parse(xhr.responseText);
     594                          if (messageContainer) {
     595                              if (response.success) {
     596                                  messageContainer.innerHTML = '<span style="color: green;">' + response.data + '</span>';
     597                              } else {
     598                                  messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
     599                              }
     600                          } else {
     601                              console.error('messageContainer element not found');
     602                          }
     603                      } catch (e) {
     604                          console.error('Failed to parse response:', e);
     605                          if (messageContainer) {
     606                              messageContainer.innerHTML = '<span style="color: red;">Error parsing response</span>';
     607                          } else {
     608                              console.error('messageContainer element not found');
     609                          }
     610                      }
     611                  } else {
     612                      console.error('Empty response');
     613                      if (messageContainer) {
     614                          messageContainer.innerHTML = '<span style="color: red;">Empty response</span>';
     615                      } else {
     616                          console.error('messageContainer element not found');
     617                      }
     618                  }
     619              } else {
     620                  console.error('Request failed with status:', xhr.status);
     621                  if (messageContainer) {
     622                      messageContainer.innerHTML = '<span style="color: red;">Error: ' + xhr.status + '</span>';
     623                  } else {
     624                      console.error('messageContainer element not found');
     625                  }
     626              }
     627 
     628              // Hide message after 3 seconds
     629              setTimeout(function() {
     630                  if (messageContainer) {
     631                      messageContainer.innerHTML = '';
     632                  }
     633              }, 3000);
     634          }
     635      };
     636     
     637 
     638      xhr.send('action=ai_app_onsite_get_accept_terms_of_service&terms_of_service=' + terms_of_service+'&ai_app_onsite_plugin_settings_terms_of_service_nonce='+terms_of_service_nonce);
     639  }
     640 
     641  function toggleAppNameSelector() {
     642    setTimeout(function() {
     643      const app_selector_input_element = document.getElementById("app_selector");
     644      const appSelectorLabel           = document.querySelector(".app_selector_input_label");
     645
     646      if (app_selector_input_element && app_selector_input_element.value != '') {
     647        appSelectorLabel.innerText         = "Edit App Name";
     648        app_selector_input_element.classList.remove('border-danger');
     649      } else {
     650        appSelectorLabel.innerHTML              = 'Create Your First App <span class="text-danger">*</span>';
     651        app_selector_input_element.classList.add('border-danger');
     652        app_selector_input_element.setAttribute('placeholder', 'Please Enter Your App Name');
     653      }
     654    }, 2000);
     655  }
     656 
     657  document.addEventListener('DOMContentLoaded', function() {
     658      const licenseKeyInfoModal         = document.getElementById("license_key_info_modal");
     659      const saveButton                  = document.getElementById("active_ai_app_plugin_license_key");
     660      const disableKeyBtn               = document.getElementById("key-disable-btn");
     661      const messageContainer            = document.getElementById("app-license-msg-container");
     662      const keyInput                    = document.getElementById('licence_key');
     663 
     664      document.addEventListener('keydown', function(event) {
     665          if (event.key === 'Enter') {
     666              event.preventDefault();
     667          }
     668      });
     669 
     670      const currentStatus = !disableKeyBtn.checked ? 'enabled' : 'disabled';
     671      disableKeyBtn.setAttribute('data-original-status', currentStatus);
     672 
     673      if (licenseKeyInfoModal) {
     674        let isHidingModal = false;
     675        let lastClickedElement = null;
     676        const modalContent = licenseKeyInfoModal.querySelector('.modal-content');
     677     
     678        const showUnsavedWarning = () => {
     679          saveButton?.classList.add("border-danger");
     680          messageContainer.innerHTML = '<span style="color: red;">You have unsaved changes, consider saving them.</span>';
     681        };
     682     
     683        const resetModalState = () => {
     684          isSwitchChecked = false;
     685          messageContainer.innerHTML = "";
     686          saveButton?.classList.remove("border-danger");
     687        };
     688     
     689        const handleCancelAction = () => {
     690          isHidingModal = true;
     691          if (isSwitchChecked) {
     692            disableKeyBtn.checked = !disableKeyBtn.checked;
     693            ai_app_onsite_disable_license_key();
     694          }
     695          resetModalState();
     696          jQuery(licenseKeyInfoModal).modal('hide');
     697          isHidingModal = false;
     698        };
     699     
     700        // Track the last clicked element
     701        document.addEventListener('mousedown', (event) => {
     702          lastClickedElement = event.target;
     703        });
     704     
     705     
     706        // Intercept dismiss logic
     707        licenseKeyInfoModal.addEventListener('hide.bs.modal', (event) => {
     708          if (isHidingModal) return;
     709     
     710          const isDismissButton = lastClickedElement?.getAttribute("data-bs-dismiss") === "modal";
     711          const isCancelButton = lastClickedElement?.id === "cancel";
     712
     713          if (isDismissButton && isCancelButton) {
     714            handleCancelAction();
     715          } else if (isSwitchChecked && lastClickedElement?.textContent?.trim() && lastClickedElement?.textContent !== "Save") {
     716            event.preventDefault();
     717            showUnsavedWarning();
     718          }
     719        });
     720      }
     721 
     722      setTimeout(function() {
     723          const app_selector_input_element  = document.getElementById("app_selector");
     724 
     725          app_selector_input_element.addEventListener("input", function () {
     726            if (app_selector_input_element.value.trim() !== "") {
     727              app_selector_input_element.classList.remove("border-danger");
     728            } else {
     729              app_selector_input_element.classList.add("border-danger");
     730            }
     731          });
     732      }, 1000);
     733 
     734      keyInput.addEventListener("input", function () {
     735          if (keyInput.value.trim() !== "") {
     736              saveButton?.removeAttribute('disabled');
     737          } else {
     738              saveButton?.setAttribute('disabled', 'true');
     739          }
     740      });
     741     
     742 
     743      var button = document.getElementById("accept_ai_app_terms_of_service");
     744      button.addEventListener("click", function () {
     745        var terms_of_service =
     746          document.getElementById("terms_conditions").checked;
     747        var terms_of_service_nonce = document.getElementById(
     748          "ai_app_onsite_plugin_settings_nonce_field"
     749        ).value;
     750        var xhr = new XMLHttpRequest(); // Initialize the XMLHttpRequest object
     751        xhr.open("POST", myPluginAjax.ajaxurl, true);
     752        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Set the request header
     753 
     754        var loader = document.getElementById("loader");
     755        loader.style.display = "flex";
     756 
     757        xhr.onreadystatechange = function () {
     758          if (xhr.readyState === 4) {
     759            // Ensure the request is complete
     760            var messageContainer =
     761              document.getElementById("Plugin-settings-msg-container") ||
     762              document.getElementById("messageContainer");
     763            if (xhr.status === 200) {
     764              if (xhr.responseText) {
     765                // Check if the response text is not empty
     766                try {
     767                  var response = JSON.parse(xhr.responseText);
     768                  if (messageContainer) {
     769                    if (response.success) {
     770                      if (isPluginSaveTriggered) {
     771                        setTimeout(function () {
     772                          ai_app_onsite_save_plugin_settings();
     773                        }, 1000);
     774                      } else {
     775                        loader.style.display = "none";
     776                        messageContainer.innerHTML =
     777                          '<span style="color: green;">' +
     778                          response.data +
     779                          "</span>";
     780                      }
     781                    } else {
     782                      loader.style.display = "none";
     783                      messageContainer.innerHTML =
     784                        '<span style="color: red;">Error: ' +
     785                        response.error +
     786                        "</span>";
     787                    }
     788                  } else {
     789                    loader.style.display = "none";
     790                    console.error("messageContainer element not found");
     791                  }
     792                } catch (e) {
     793                  loader.style.display = "none";
     794                  console.error("Failed to parse response:", e);
     795                  if (messageContainer) {
     796                    messageContainer.innerHTML =
     797                      '<span style="color: red;">Error parsing response</span>';
     798                  } else {
     799                    console.error("messageContainer element not found");
     800                  }
     801                }
     802              } else {
     803                loader.style.display = "none";
     804                console.error("Empty response");
     805                if (messageContainer) {
     806                  messageContainer.innerHTML =
     807                    '<span style="color: red;">Empty response</span>';
     808                } else {
     809                  console.error("messageContainer element not found");
     810                }
     811              }
     812            } else {
     813              loader.style.display = "none";
     814              console.error("Request failed with status:", xhr.status);
     815              if (messageContainer) {
     816                messageContainer.innerHTML =
     817                  '<span style="color: red;">Error: ' + xhr.status + "</span>";
     818              } else {
     819                console.error("messageContainer element not found");
     820              }
     821            }
     822 
     823            // Hide message after 3 seconds
     824            setTimeout(function () {
     825              if (messageContainer) {
     826                messageContainer.innerHTML = "";
     827              }
     828            }, 3000);
     829          }
     830        };
     831 
     832        xhr.send(
     833          "action=ai_app_onsite_get_accept_terms_of_service&terms_of_service=" +
     834            terms_of_service +
     835            "&ai_app_onsite_plugin_settings_terms_of_service_nonce=" +
     836            terms_of_service_nonce
     837        );
     838      });
     839 
     840      ai_app_onsite_get_plugin_settings();
     841 
     842      var licenseKeychange = document.getElementById('licence_key')
     843      if (licenseKeychange) {
     844          licenseKeychange.onchange = handleInputChange;
     845      }
     846  });
     847  /*=============End download user stats js=================================== */
     848 
     849  // LICENSE SETTING
     850  const keyNote              = document.getElementById('key_note');
     851  const licenseStatus        = document.getElementById('license_status');
     852  const loader               = document.getElementById('loader');
     853  const statusElem           = document.getElementById('license_key_status');
     854  const messageContainer     = document.getElementById('app-license-msg-container');
     855  const keyInput             = document.getElementById('licence_key');
     856  const keyInputHd           = document.getElementById('licence_key_hidden');
     857  const expiryElem           = document.getElementById('license_key_expiry_date');
     858  const keyDisableStatusElem = document.getElementById('license_key_disabled_status');
     859  const removeBtn            = document.getElementById('remove_ai_app_plugin_license_key');
     860  const expiryStatus         = document.getElementById('expiry_status');
     861  const keyStatus            = document.getElementById('key_status');
     862  const disableKeyBtn        = document.getElementById("key-disable-btn");
     863  const licenseTextSwitch    = document.getElementById('license-text-switch');
     864 
     865  function handleInputChange() {
     866    if (keyInput.value) {
     867      keyInputHd.value = keyInput.value;
     868
     869      ai_app_onsite_app_verify_license_key();
     870    }
     871  }
     872 
     873  // Initial state
     874  setUIState('default');
     875 
     876  //====================
     877  // Helper Functions
     878  //====================
     879 
     880  function formatDate(dateString) {
     881      return new Date(dateString).toLocaleDateString('en-US', {
     882          year: 'numeric',
     883          month: 'long',
     884          day: 'numeric',
     885      });
     886  }
     887 
     888  function ai_app_onsite_disable_license_key() {
     889      // Enable or diabled License key status
     890      const switch_status = disableKeyBtn.checked;
     891      if(switch_status){
     892          licenseTextSwitch.textContent = 'Enabled';
     893          licenseTextSwitch.style.color = 'green';
     894          isSwitchChecked = true; // Mark as toggled
     895      } else {
     896          licenseTextSwitch.textContent = 'Disabled';
     897          licenseTextSwitch.style.color = 'red';
     898          isSwitchChecked = true; // Mark as toggled
     899      }
     900  }
     901 
     902  function setUIState(state, data = {}) {
     903      switch (state) {
     904          case 'active':
     905          case 'inactive':
     906              const isActive = state === 'active';
     907              keyInput.disabled = true;
     908              licenseStatus.style.display = 'block';
     909              expiryStatus.style.display = 'block';
     910              keyStatus.style.display = 'block';
     911              statusElem.textContent = isActive ? 'ACTIVE' : 'EXPIRED';
     912              statusElem.style.color = isActive ? 'green' : 'red';
     913              keyNote.style.display = 'none';
     914              removeBtn.classList.toggle('d-flex', true);
     915              removeBtn.disabled = false;
     916              expiryElem.textContent = formatDate(data.expiry);
     917              keyDisableStatusElem.textContent = data.key_disable_status == 'false' ? 'Enabled' : 'Disabled';
     918              keyDisableStatusElem.style.color = data.key_disable_status == 'false' ? 'green' : 'red';
     919              disableKeyBtn.disabled = false;
     920              disableKeyBtn.checked = data.key_disable_status == 'false';
     921              licenseTextSwitch.textContent = data.key_disable_status == 'false' ? 'Enabled' : 'Disabled';
     922              licenseTextSwitch.style.color = data.key_disable_status == 'false' ? 'green' : 'red';
     923              break;
     924 
     925          case 'default':
     926          default:
     927              keyInput.disabled = false;
     928              keyInput.value = '';
     929              keyInputHd.value = '';
     930              licenseStatus.style.display = 'none';
     931              expiryStatus.style.display = 'none';
     932              keyStatus.style.display = 'none';
     933              keyNote.style.display = 'block';
     934              removeBtn.classList.toggle('d-flex', true);
     935              removeBtn.disabled = true;
     936              expiryElem.textContent = '';
     937              keyDisableStatusElem.textContent = '';
     938              disableKeyBtn.checked = false;
     939              disableKeyBtn.disabled = true;
     940              licenseTextSwitch.textContent = 'Disabled';
     941              licenseTextSwitch.style.color = 'red';
     942              break;
     943      }
     944  }
     945 
     946  function sendAjax(action, onSuccess) {
     947      const xhr = new XMLHttpRequest();
     948      xhr.open('POST', myPluginAjax.ajaxurl, true);
     949      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
     950 
     951      xhr.onreadystatechange = () => {
     952          if (xhr.readyState !== 4) return;
     953          if (xhr.status === 200) {
     954              try {
     955                  const response = JSON.parse(xhr.responseText);
     956                  onSuccess(response);
     957              } catch (err) {
     958                  console.error('Invalid JSON response:', err);
     959                  alert('Unexpected response from server.');
     960              }
     961          } else {
     962              alert(`Error: ${xhr.status} ${xhr.statusText}`);
     963          }
     964 
     965          const licenseFormTracker = trackFormChanges('#license-validation-form');
     966          window.FormChangeRegistry.register('license_settings', licenseFormTracker, 'app-settings');
     967 
     968          document.getElementById('active_ai_app_plugin_license_key').addEventListener('click', function () {
     969              licenseFormTracker.reset();
     970          });
     971      };
     972 
     973      xhr.send(`action=${encodeURIComponent(action)}`);
     974  }
     975 
     976  //====================
     977  // Fetch License Key
     978  //====================
     979  function ai_app_onsite_get_app_license_key() {
     980      const saveButton = document.getElementById("active_ai_app_plugin_license_key");
     981 
     982      sendAjax('ai_app_onsite_get_app_license_key', (response) => {
     983          if (response.status === '404') {
     984              saveButton?.setAttribute('disabled', 'true');
     985              return;
     986          }
     987 
     988          const { license_key, license_key_hd, key_status, subscription_expiry_date, key_disable_status } = response[0] || {};
     989   
     990          keyInput.value = license_key || '';
     991          keyInputHd.value = license_key_hd || '';
     992 
     993          if (key_status === 'ACTIVE') {
     994              saveButton?.removeAttribute('disabled');
     995              setUIState('active', { expiry: subscription_expiry_date, key_disable_status: key_disable_status });
     996          } else if (key_status === 'INACTIVE' && license_key) {
     997              saveButton?.removeAttribute('disabled');
     998              setUIState('inactive', { expiry: subscription_expiry_date, key_disable_status: key_disable_status });
     999          } else {
     1000              saveButton?.setAttribute('disabled', 'true');
     1001              setUIState('default');
     1002          }
     1003          toggleAppNameSelector();
     1004      });
     1005  }
     1006 
     1007  //====================
     1008  // Remove License Key
     1009  //====================
     1010  function ai_app_onsite_app_remove_license_key() {
     1011      if (confirm("Warning: Removing the premium access key may cause your existing app to lose functionality. All PLUS features will be disabled within 24 hours. Are you sure you want to proceed?")) {
     1012          loader.style.display = 'flex';
     1013 
     1014          sendAjax('ai_app_onsite_app_remove_license_key', (response) => {
     1015              loader.style.display = 'none';
     1016              location.reload(true);
     1017              setUIState('default');
     1018          });
     1019      }
     1020  }
     1021 
     1022  //====================
     1023  // Disable/Enable License Key
     1024  //====================
     1025  function ai_app_onsite_app_toggle_license_key_status(status) {
     1026      loader.style.display = 'flex';
     1027 
     1028      const xhr = new XMLHttpRequest();
     1029          xhr.open('POST', myPluginAjax.ajaxurl, true);
     1030 
     1031          xhr.onreadystatechange = () => {
     1032              loader.style.display = 'none';
     1033 
     1034              if (xhr.readyState !== 4) return;
     1035              if (xhr.status === 200) {
     1036                  try {
     1037                      const response = JSON.parse(xhr.responseText);
     1038                  } catch (err) {
     1039                      console.error('Invalid JSON response:', err);
     1040                  }
     1041              } else {
     1042                  alert(`Error: ${xhr.status} ${xhr.statusText}`);
     1043              }
     1044          };
     1045 
     1046          const formData = new FormData();
     1047          formData.append('action', 'ai_app_onsite_app_toggle_license_key_status');
     1048          formData.append('status', status);
     1049     
     1050          xhr.send(formData);
     1051  }
     1052 
     1053 
     1054  //========[ Validate & Save License Key ]========//
     1055function ai_app_onsite_app_verify_license_key(close_modal =  false) {
     1056      const loader = document.getElementById('loader');
     1057      const saveButton                  = document.getElementById("active_ai_app_plugin_license_key");
     1058     
     1059      let licenceKey = document.getElementById('licence_key').value.trim();
     1060      const licenceKeyHidden = document.getElementById('licence_key_hidden').value.trim();
     1061      if (licenceKeyHidden && licenceKeyHidden.trim() !== '') {
     1062          licenceKey = licenceKeyHidden;
     1063      }
     1064      const status = !disableKeyBtn.checked ? 'true' : 'false';
     1065      const nonce = document.getElementById("ai_app_onsite_app_verify_license_key_nonce_field").value;
     1066 
     1067      // Clear error messages
     1068      document.querySelectorAll('.fields-error').forEach(el => el.textContent = '');
     1069      loader.style.display = 'flex';
     1070 
     1071      const xhr = new XMLHttpRequest();
     1072      xhr.open('POST', myPluginAjax.ajaxurl, true);
     1073 
     1074      xhr.onreadystatechange = () => {
     1075          if (xhr.readyState !== 4) return;
     1076 
     1077        setTimeout(function () {
     1078          loader.style.display = 'none';
     1079 
     1080          let response;
     1081          try {
     1082              response = JSON.parse(xhr.responseText);
     1083          } catch (e) {
     1084              messageContainer.innerHTML = `<span style="color: red;">Invalid server response</span>`;
     1085              return;
     1086          }
     1087 
     1088          if (xhr.status === 200) {
     1089              if (response.success) {
     1090                  saveButton?.classList.remove("border-danger");
     1091                  ai_app_onsite_get_app_license_key();
     1092                  messageContainer.innerHTML = `<span style="color: green;">${response.data}</span>`;
     1093                  close_modal = true;
     1094              } else {
     1095                close_modal = false;
     1096                messageContainer.innerHTML = `<span style="color: red;">Error: ${response.data}</span>`;
     1097              }
     1098          } else {
     1099            close_modal = false;
     1100            messageContainer.innerHTML = `<span style="color: red;">Error: Failed to verify data</span>`;
     1101          }
     1102 
     1103          setTimeout(function () {
     1104            if(close_modal){
     1105                var modal = document.getElementById("license_key_info_modal");
     1106                jQuery(modal).modal("hide");
     1107                location.reload(true);
     1108                messageContainer.innerHTML = "";
     1109            }
     1110           
     1111          }, 3000);
     1112        }, 2000);
     1113      };
     1114 
     1115      const formData = new FormData();
     1116      formData.append('action', 'ai_app_onsite_app_verify_license_key');
     1117      formData.append('licence_key', licenceKey);
     1118      formData.append('status', status);
     1119      formData.append('ai_app_onsite_app_verify_license_key_nonce_field', nonce);
     1120 
     1121      xhr.send(formData);
     1122  }
     1123 
     1124  window.FormChangeRegistry = {
     1125      trackers: {},
     1126      tabMap: {},
     1127 
     1128      register: function (key, tracker, tabKey) {
     1129          this.trackers[key] = tracker;
     1130 
     1131          if (tabKey) {
     1132              if (!this.tabMap[tabKey]) this.tabMap[tabKey] = [];
     1133              this.tabMap[tabKey].push(key);
     1134          }
     1135      },
     1136 
     1137      isAnyDirty: function () {
     1138          return Object.values(this.trackers).some(t => t.hasUnsavedChanges());
     1139      },
     1140 
     1141      isTabDirty: function (tabKey) {
     1142          const tabKeys = this.tabMap[tabKey] || [];
     1143          return tabKeys.some(key => this.trackers[key]?.hasUnsavedChanges());
     1144      },
     1145 
     1146      resetTab: function (tabKey) {
     1147          const tabKeys = this.tabMap[tabKey] || [];
     1148          tabKeys.forEach(key => this.trackers[key]?.reset());
    1221149      }
    1231150  };
    124 
    125   xhr.open('POST', myPluginAjax.ajaxurl, true);
    126 
    127   console.log(document.getElementById("app_email").value);
    128 
    129   var formData = new FormData();
    130     formData.append('action', 'ai_app_onsite_save_plugin_settings');
    131     formData.append('terms_of_service', document.getElementById("terms_of_service-old") ? document.getElementById("terms_of_service-old").checked : false);
    132     formData.append('app_selector', document.getElementById("app_selector") ? document.getElementById("app_selector").value : "");
    133     formData.append('app_email', document.getElementById("app_email") ? document.getElementById("app_email").value : "");
    134 
    135 
    136     // Gather selected banned words
    137     var bannedWordsSelect = document.querySelector('[data-multi-select-plugin]');
    138     var selectedOptions = bannedWordsSelect.selectedOptions;
    139     var banned_words = [];
    140     let selectedLabels = document.querySelectorAll('.selected-wrapper .selected-label');
    141 
    142       // Loop through each of the selected labels
    143     selectedLabels.forEach(function(label) {
    144         banned_words.push(label.textContent);
    145     });
    146 // Append each banned word to the formData object
    147     for (var j = 0; j < banned_words.length; j++) {
    148         formData.append('banned_words[]', banned_words[j]);
    149     }
    150 
    151 
    152     // Append the CSV file
    153     const fileInput = document.getElementById('csv-upload');
    154     if (fileInput.files.length > 0) {
    155         const file = fileInput.files[0];
    156         formData.append('csv-file', file);
    157     }
    158 
    159     // Append nonce field value
    160     formData.append('ai_app_onsite_plugin_settings_nonce_field', document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value);
    161 
    162        
    163   xhr.send(formData);
    164 }
    165 
    166 //=======================End save plugin settings js===========================
    167 
    168 //Start get plugin setting from db js
    169 function ai_app_onsite_get_plugin_settings() {
    170     var xhr = new XMLHttpRequest();
    171 
    172     xhr.onreadystatechange = function() {
    173         if (xhr.readyState === 4) {
    174            
    175             if (xhr.status === 200) {
    176                     // Handle success response
    177                     var response = JSON.parse(xhr.responseText);
    178                   if(response.status === '404'){
    179                     //jQuery('#terms_conditions_modal').modal('show');
    180                     const modalElement = document.getElementById('terms_conditions_modal');
    181                     const modalInstance = new bootstrap.Modal(modalElement);
    182                     modalInstance.show();
    183 
    184 
    185                     return;
    186                   }
    187 
    188                     // Accessing the first object in the array
    189                     var firstObject = response[0];
    190 
    191                     // Accessing properties of the first object
    192                     var id             = firstObject.id;
    193                     var agreeTerms     = firstObject.agree_terms;
    194                     var appSelector    = firstObject.app_selector;
    195                     var userStats      = firstObject.user_stats;
    196                     var bannedWords    = firstObject.banned_words;
    197                     var bannedWordsCSV = firstObject.banned_words_csv;
    198                     var appName        = firstObject.appName;
    199                     const selectElement = document.getElementById('bannedWords');
    200 
    201                     var app_email = firstObject.app_email;
    202 
    203 
    204                    // Assume selectElement is already defined as your target select element
    205                    bannedWordsCSV.forEach(item => {
    206                        // Check if an option with the same value already exists
    207                        const exists = Array.from(selectElement.options).some(option => option.value === item[0]);
    208                        
    209                        if (!exists) {
    210                            // Create a new option element
    211                            const option = document.createElement('option');
    212                            
    213                            // Set the value and text of the option element
    214                            option.value = item[0];
    215                            option.text = item[0];
    216                            
    217                            // Append the option element to the select element
    218                            selectElement.appendChild(option);
    219                        }
    220                    });
    221 
    222                     if (appName != null && appName !== '' && appName !== 'undefined') {
    223                         var selectElements = document.getElementById('app_selector');
    224                         selectElements.value = appName;
    225                     }
    226                     var app_emails = document.getElementById('app_email');
    227                         app_emails.value = app_email;
    228 
    229                     // Set checkbox checked if agreeTerms is "1"
    230                     if (agreeTerms === "1") {
    231                         document.getElementById('terms_of_service-old').checked = true;
    232                         // Ensure the modal is not triggered
    233                     } else {
    234                         // Show the modal if terms are not agreed to
    235                         const modalElement = document.getElementById('terms_conditions_modal');
    236                         const modalInstance = new bootstrap.Modal(modalElement);
    237                         modalInstance.show();
    238                     }
    239                
    240                     var searchContainer = document.querySelector('.search-container');
    241 
    242                     console.log('bannedWords', bannedWords);
    243 //
    244                 // document.addEventListener('DOMContentLoaded', function() {
    245                      var searchContainer = document.querySelector('.search-container'); // Ensure the selector is correct
    246                      if (!searchContainer) {
    247                          console.error('searchContainer element not found!');
    248                          return; // Exit if the container is missing
    249                      }
    250                      bannedWords.forEach(function(value) {
    251                          console.log('value', value);
    252                          if (value) { // Ensure value is not empty or falsy
    253                              var exists = document.querySelector('.selected-wrapper span.selected-label[data-value="' + value + '"]');
    254                              if (!exists) {
    255                                  // Create new elements only if the value is not empty and doesn't already exist
    256                                  var newDiv = document.createElement('div');
    257                                  newDiv.className = 'selected-wrapper';
    258 
    259                                  var labelSpan = document.createElement('span');
    260                                  labelSpan.className = 'selected-label';
    261                                  labelSpan.textContent = value;
    262                                  labelSpan.setAttribute('data-value', value);
    263 
    264                                  var closeLink = document.createElement('a');
    265                                  closeLink.className = 'selected-close';
    266                                  closeLink.setAttribute('tabindex', '-1');
    267                                  closeLink.setAttribute('data-option', value);
    268                                  closeLink.setAttribute('data-hits', '0');
    269                                  closeLink.href = '#';
    270                                  closeLink.textContent = 'x';
    271 
    272                                  newDiv.appendChild(labelSpan);
    273                                  newDiv.appendChild(closeLink);
    274                                  searchContainer.before(newDiv); // Ensure searchContainer exists before this line
    275                              }
    276                          }
    277                      });
    278                 // });
    279 
    280 
    281 
    282 
    283 
    284                 document.querySelectorAll('.selected-wrapper').forEach(wrapper => {
    285                    const label = wrapper.querySelector('.selected-label');
    286                    if (!label.hasAttribute('data-value')) {
    287                        wrapper.remove();
    288                    }
    289                 });   
    290 
    291 
    292                     // Event listener for the dropdown icon click
    293                 const dropdownIcon = document.querySelector('.dropdown-icon');
    294                     if (dropdownIcon) {
    295                         dropdownIcon.addEventListener('click', function() {
    296                            console.log('Dropdown icon clicked');
    297                            if (this.classList.contains('active')) {
    298                                console.log('Dropdown icon is active');
    299                                document.querySelectorAll('.selected-wrapper .selected-label').forEach(function(element) {
    300                                    var valueToRemove = element.textContent.trim();
    301                                    console.log('Removing:', valueToRemove);
    302                                    document.querySelectorAll('.autocomplete-list li').forEach(function(liElement) {
    303                                        if (liElement.textContent.trim() === valueToRemove) {
    304                                            console.log('Found and removing li:', liElement.textContent.trim());
    305                                            liElement.parentNode.removeChild(liElement);
    306                                        }
    307                                    });
    308                                });
    309                            } else {
    310                                console.log('Dropdown icon is not active');
    311                            }
    312                        });
    313                    } else {
    314                        console.log('Dropdown icon not found');
    315                    }
    316              
    317 
    318 
    319 
    320 
    321             } else {
    322                 // Handle error response
    323                 alert('Error occurred: ' + xhr.responseText);
    324             }
    325         }
    326     };
    327 
    328     xhr.open('POST', myPluginAjax.ajaxurl, true);
    329     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    330     xhr.send('action=ai_app_onsite_get_plugin_settings');
    331 }
    332 
    333 ai_app_onsite_get_plugin_settings();
    334 /*=============End get plugin setting from db js============================== */
    335 
    336 
    337 /*=============Download terms Of service js=================================== */
    338 
    339 function ai_app_onsite_download_terms_Of_service() {
    340     const { jsPDF } = window.jspdf;
    341     const doc = new jsPDF();
    342     const termsContent = document.getElementById('terms-conditions-modal-pdf').innerText;
    343 
    344     const maxWidth = 180; // Maximum width for text in mm
    345         const startX = 10; // X-coordinate to start text
    346         const startY = 10; // Y-coordinate to start text
    347         const lineHeight = 4.5; // Approximate line height in mm
    348         const pageHeight = 297; // A4 page height in mm (210x297mm)
    349         const marginBottom = 10; // Bottom margin
    350 
    351         let cursorY = startY; // Track current Y-coordinate
    352 
    353         // Set font size
    354         const fontSize = 12; // Set the font size as desired
    355         doc.setFontSize(fontSize);
    356 
    357         // Split text into lines that fit within the specified max width
    358         const lines = doc.splitTextToSize(termsContent, maxWidth);
    359 
    360         // Add the lines of text to the PDF
    361         lines.forEach(line => {
    362             if (cursorY + lineHeight > pageHeight - marginBottom) {
    363                 // Add new page if the text exceeds the page height
    364                 doc.addPage();
    365                 cursorY = startY; // Reset cursorY for new page
    366             }
    367             doc.text(line, startX, cursorY);
    368             cursorY += lineHeight * (fontSize / 10); // Adjust cursorY based on font size
    369         });
    370     doc.save('AIappOniste-terms-of-service.pdf');
    371 }
    372 
    373 /*=============End download terms Of service js=================================== */
    374 
    375 
    376 /*=============Download user stats js=================================== */
    377 function ai_app_onsite_download_user_stats(event) {
    378     event.preventDefault();
    379     // Show loader
    380     document.getElementById('loader').style.display = 'flex';
    381     var messageContainer = document.getElementById('Plugin-settings-msg-container');
    382 
    383     // Get the selected month (e.g., from a dropdown or an input)
    384     const month = document.getElementById('ai_app_onsite-user-stats').value;
    385     var userStatsError = document.getElementById('ai_app_onsite-user-stats-error');
    386     userStatsError.textContent = '';
    387     var ai_app_onsite_download_user_stats_nonce = myPluginAjax.downloadUserStatsNonce;
    388    
    389     // Prepare data to send
    390    const data = new FormData();
    391     data.append('ai_app_onsite_download_user_stats_nonce', ai_app_onsite_download_user_stats_nonce); // Correctly use the nonce variable
    392     data.append('action', 'ai_app_onsite_download_user_stats'); // Action name
    393     data.append('month', month); // Pass the month value
    394     // Make AJAX request
    395     fetch(ajaxurl, {
    396         method: 'POST',
    397         body: data,
    398     })
    399     .then(response => {
    400         if (!response.ok) {
    401             throw new Error('Network response was not ok');
    402         }
    403         return response.text(); // Read response as text
    404     })
    405     .then(text => {
    406         // Hide loader after a delay (e.g., 1 second)
    407         setTimeout(() => {
    408             document.getElementById('loader').style.display = 'none';
    409 
    410             // Check if the response indicates failure and no data found
    411             if (text.includes('No user statistics found.')) {
    412                 // Show error message
    413                 userStatsError.textContent = 'No user statistics found.';
    414             } else {
    415                 // Create a blob from the response text
    416                 const blob = new Blob([text], { type: 'text/csv' });
    417 
    418                 // Create a link element
    419                 const url = window.URL.createObjectURL(blob);
    420                 const a = document.createElement('a');
    421                 a.style.display = 'none';
    422                 a.href = url;
    423                 a.download = 'AIappOniste_User_Stats.csv';
    424 
    425                 // Append the link to the body
    426                 document.body.appendChild(a);
    427                 a.click();
    428 
    429                 // Remove the link
    430                 document.body.removeChild(a);
    431 
    432                 // Revoke the object URL
    433                 window.URL.revokeObjectURL(url);
    434 
    435                 console.log('User stats downloaded successfully');
    436                 messageContainer.innerHTML = '<span style="color: green;">User stats downloaded successfully</span>';
    437 
    438             }
    439             setTimeout(function() {
    440                 messageContainer.innerHTML = '';
    441             }, 3000);
    442         }, 2000); // Adjust delay time as needed
    443     })
    444     .catch(error => {
    445         // Hide loader
    446         document.getElementById('loader').style.display = 'none';
    447 
    448         // Show error message
    449         document.getElementById('ai_app_onsite-user-stats-error').textContent = 'Error: ' + error.message;
    450     });
    451 }
    452 
    453 // Function to clear error message when month selection is changed
    454 // document.getElementById('ai_app_onsite-user-stats').addEventListener('change', function() {
    455 //     document.getElementById('ai_app_onsite-user-stats-error').textContent = '';
    456 // });
    457 
    458 //upload file js
    459 function validateCSVFile(input) {
    460     var file = document.getElementById('csv-upload').files[0];
    461     var fileError = document.getElementById('fileError');
    462     // Check if file is selected
    463     if (!file) {
    464         fileError.textContent = 'Please select a file.';
    465        return;
    466     }
    467 
    468     // Check file type
    469     var validExtensions = ['text/csv'];
    470     if (!validExtensions.includes(file.type)) {
    471         fileError.textContent = 'Please select a valid image file (SVG, PNG, JPG, or GIF).';
    472         this.value = ''; // Clear the input field
     1151 
     1152  let pendingTabSwitch = null;
     1153  let currentTab = null;
     1154 
     1155  document.querySelectorAll('.plugin-tab').forEach(tab => {
     1156      tab.addEventListener('click', function (e) {
     1157          e.preventDefault();
     1158 
     1159          const targetTabId = this.getAttribute('data-tab-id');
     1160          const targetTab = this.getAttribute('data-form-tab');
     1161          const activeTab = document.querySelector('.plugin-tab.nav-tab-active')?.getAttribute('data-form-tab');
     1162 
     1163          // window.FormChangeRegistry.trackers[activeTab]?.deactivate();
     1164          // window.FormChangeRegistry.trackers[targetTab]?.activate();
     1165 
     1166          if (activeTab && window.FormChangeRegistry.isTabDirty(activeTab)) {
     1167              pendingTabSwitch = targetTab;
     1168              currentTab = activeTab;
     1169 
     1170              // Store targetTabId to proceed later
     1171              window._deferredTabClick = targetTabId;
     1172 
     1173              const modal = new bootstrap.Modal(document.getElementById('unsavedChangesModal'));
     1174              modal.show();
     1175          } else {
     1176              handleTabClick(targetTabId);
     1177          }
     1178      });
     1179  });
     1180 
     1181  document.querySelectorAll('.dismiss-unsaved-modal').forEach(button => {
     1182      button.addEventListener('click', function () {
     1183          const modalElement = document.getElementById('unsavedChangesModal');
     1184          const modalInstance = bootstrap.Modal.getInstance(modalElement);
     1185 
     1186          if (modalInstance) {
     1187              modalInstance.hide();
     1188 
     1189              setTimeout(() => {
     1190                  if (!document.querySelector('.modal.show')) {
     1191                      document.querySelectorAll('.modal-backdrop').forEach(el => el.remove());
     1192                      document.body.classList.remove('modal-open');
     1193                      document.body.style.overflow = '';
     1194                  }
     1195 
     1196                  window.FormChangeRegistry.resetTab(currentTab);
     1197 
     1198                  if (window._deferredTabClick) {
     1199                      handleTabClick(window._deferredTabClick);
     1200                      window._deferredTabClick = null;
     1201                      currentTab = null;
     1202                      pendingTabSwitch = null;
     1203                  }
     1204              }, 400);
     1205          }
     1206      });
     1207  });
     1208 
     1209  document.querySelectorAll('.save-changes-modal').forEach(button => {
     1210      button.addEventListener('click', function () {
     1211          const modalElement = document.getElementById('unsavedChangesModal');
     1212          const modalInstance = bootstrap.Modal.getInstance(modalElement);
     1213 
     1214          if (modalInstance) {
     1215              switch (currentTab) {
     1216                  case 'app-settings':
     1217                      ai_app_onsite_save_plugin_settings();
     1218                      break;
     1219                  case 'app-details':
     1220                      ai_app_onsite_save_app_properties();
     1221                      break;
     1222                  case 'model-settings':
     1223                      ai_app_onsite_save_model_settings();
     1224                      break;
     1225                  case 'field-selector':
     1226                      ai_app_onsite_save_ai_app_fieldselector_data();
     1227                      break;
     1228                  case 'prompt-editor':
     1229                      ai_app_onsite_save_prompt_editor();
     1230                      break;
     1231                  case 'app-preview':
     1232                      ai_app_onsite_update_style_properties_only();
     1233                      break;
     1234                  default:
     1235                      console.warn('No matching save method for the current tab:', currentTab);
     1236              }
     1237 
     1238              modalInstance.hide();
     1239 
     1240              setTimeout(() => {
     1241                  if (!document.querySelector('.modal.show')) {
     1242                      document.querySelectorAll('.modal-backdrop').forEach(el => el.remove());
     1243                      document.body.classList.remove('modal-open');
     1244                      document.body.style.overflow = '';
     1245                  }
     1246 
     1247                  window.FormChangeRegistry.resetTab(currentTab);
     1248 
     1249                  if (window._deferredTabClick) {
     1250                      handleTabClick(window._deferredTabClick);
     1251                      window._deferredTabClick = null;
     1252                      currentTab = null;
     1253                      pendingTabSwitch = null;
     1254                  }
     1255              }, 400);
     1256          }
     1257      });
     1258  });
     1259 
     1260  function activatePluginTab(tabName) {
     1261      document.querySelectorAll('.plugin-tab-content').forEach(el => el.classList.add('d-none'));
     1262      document.getElementById(`tab-content-${tabName}`)?.classList.remove('d-none');
     1263 
     1264      document.querySelectorAll('.plugin-tab').forEach(el => el.classList.remove('nav-tab-active'));
     1265      document.querySelector(`.plugin-tab[data-form-tab="${tabName}"]`)?.classList.add('nav-tab-active');
     1266  }
     1267 
     1268  // Tracker Utility
     1269  function trackFormChanges(formSelector, options = {}) {
     1270      const form = typeof formSelector === 'string' ? document.querySelector(formSelector) : formSelector;
     1271      if (!form) return;
     1272 
     1273      const config = {
     1274          warnOnLeave: false,
     1275          customWarningMessage: 'You have unsaved changes.',
     1276          onChange: null,
     1277          ...options,
     1278      };
     1279 
     1280      let initialState = {};
     1281      let isDirty = false;
     1282 
     1283      const getFieldValue = (field) => {
     1284          if (field.type === 'color') return;
     1285          if (field.type === 'radio') {
     1286              const selected = document.querySelector(`input[name="${field.name}"]:checked`);
     1287              return selected ? selected.value : '';
     1288          }
     1289          if (field.type === 'checkbox') {
     1290              return field.checked;
     1291          }
     1292          if (field.type === 'file') {
     1293              return Array.from(field.files).map(file => file.name).join(',') || '';
     1294          }
     1295          if (field.tagName === 'TEXTAREA') {
     1296              return field.value.trim();
     1297          }
     1298          if (field.tagName === 'SELECT') {
     1299              if (field.multiple) {
     1300                  return Array.from(field.selectedOptions).map(option => option.value).join(',');
     1301              }
     1302              return field.value;
     1303          }
     1304          return field.value.trim();
     1305      };
     1306 
     1307      const getCurrentFormState = () => {
     1308          const state = {};
     1309          form.querySelectorAll('input, textarea, select').forEach(field => {
     1310              if (field.name) {
     1311                  const value = getFieldValue(field);
     1312                  if (value !== undefined) {
     1313                      state[field.name] = value;
     1314                  }
     1315              }
     1316          });
     1317          return state;
     1318      };
     1319 
     1320      const captureInitialState = () => {
     1321          initialState = getCurrentFormState();
     1322      };
     1323 
     1324      const hasChanged = () => {
     1325          const currentState = getCurrentFormState();
    4731326     
    474         return;
    475     }
    476 
    477     fileError.textContent = ''; // Clear any previous errors
    478 
    479        uploadCSVFile(file);
    480          // Reset input value to allow same file to be selected again
    481     input.value = '';
    482 }
    483 
    484 
    485 function uploadCSVFile(file) {
    486     var formData = new FormData();
    487     formData.append('csv-file', file);
    488     formData.append('action', 'ai_app_onsite_handle_csv_upload');
    489     formData.append('ai_app_onsite_plugin_settings_nonce_csv_upload_field', document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value);
    490 
    491     var xhr = new XMLHttpRequest();
    492     xhr.open('POST', myPluginAjax.ajaxurl, true);
    493    
    494     xhr.onreadystatechange = function () {
    495         if (xhr.readyState === 4) { // Ensure the request is complete
    496             if (xhr.status === 200) {
    497                 console.log('Response Text:', xhr.responseText); // Log the raw response text
    498 
    499                 if (xhr.responseText) { // Check if the response text is not empty
    500                     try {
    501                         var response = JSON.parse(xhr.responseText);
    502                         var messageContainer = document.getElementById('Plugin-settings-msg-container');
    503                         if (messageContainer) {
    504                             if (response.success) {
    505                                 messageContainer.innerHTML = '<span style="color: green;">' + response.data + '</span>';
    506                             } else {
    507                                 messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
    508                             }
    509                              ai_app_onsite_get_plugin_settings();   
    510 
    511                         } else {
    512                             console.error('messageContainer element not found');
    513                         }
    514                     } catch (e) {
    515                         console.error('Failed to parse response:', e);
    516                         var messageContainer = document.getElementById('messageContainer');
    517                         if (messageContainer) {
    518                             messageContainer.innerHTML = '<span style="color: red;">Error parsing response</span>';
    519                         } else {
    520                             console.error('messageContainer element not found');
    521                         }
    522                     }
    523                 } else {
    524                     console.error('Empty response');
    525                     var messageContainer = document.getElementById('messageContainer');
    526                     if (messageContainer) {
    527                         messageContainer.innerHTML = '<span style="color: red;">Empty response</span>';
    528                     } else {
    529                         console.error('messageContainer element not found');
    530                     }
    531                 }
    532             } else {
    533                 console.error('Request failed with status:', xhr.status);
    534                 var messageContainer = document.getElementById('messageContainer');
    535                 if (messageContainer) {
    536                     messageContainer.innerHTML = '<span style="color: red;">Error: ' + xhr.status + '</span>';
    537                 } else {
    538                     console.error('messageContainer element not found');
    539                 }
    540             }
    541             // Hide message after 3 seconds
    542             setTimeout(function() {
    543                 messageContainer.innerHTML = '';
    544             }, 3000);
    545 
    546 
    547         }
    548     };
    549 
    550     xhr.send(formData);
    551 }
    552 
    553 function uploadTermCondition(checkbox) {
    554     var terms_of_service = document.getElementById("terms_of_service-old").checked;
    555     var terms_of_service_nonce = document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value;
    556     var xhr = new XMLHttpRequest(); // Initialize the XMLHttpRequest object
    557     xhr.open('POST', myPluginAjax.ajaxurl, true);
    558     xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Set the request header
    559 
    560     xhr.onreadystatechange = function () {
    561         if (xhr.readyState === 4) { // Ensure the request is complete
    562             var messageContainer = document.getElementById('Plugin-settings-msg-container') || document.getElementById('messageContainer');
    563             if (xhr.status === 200) {
    564                 console.log('Response Text:', xhr.responseText); // Log the raw response text
    565 
    566                 if (xhr.responseText) { // Check if the response text is not empty
    567                     try {
    568                         var response = JSON.parse(xhr.responseText);
    569                         if (messageContainer) {
    570                             if (response.success) {
    571                                 messageContainer.innerHTML = '<span style="color: green;">' + response.data + '</span>';
    572                             } else {
    573                                 messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
    574                             }
    575                         } else {
    576                             console.error('messageContainer element not found');
    577                         }
    578                     } catch (e) {
    579                         console.error('Failed to parse response:', e);
    580                         if (messageContainer) {
    581                             messageContainer.innerHTML = '<span style="color: red;">Error parsing response</span>';
    582                         } else {
    583                             console.error('messageContainer element not found');
    584                         }
    585                     }
    586                 } else {
    587                     console.error('Empty response');
    588                     if (messageContainer) {
    589                         messageContainer.innerHTML = '<span style="color: red;">Empty response</span>';
    590                     } else {
    591                         console.error('messageContainer element not found');
    592                     }
    593                 }
    594             } else {
    595                 console.error('Request failed with status:', xhr.status);
    596                 if (messageContainer) {
    597                     messageContainer.innerHTML = '<span style="color: red;">Error: ' + xhr.status + '</span>';
    598                 } else {
    599                     console.error('messageContainer element not found');
    600                 }
    601             }
    602 
    603             // Hide message after 3 seconds
    604             setTimeout(function() {
    605                 if (messageContainer) {
    606                     messageContainer.innerHTML = '';
    607                 }
    608             }, 3000);
    609         }
    610     };
    611    
    612 
    613     xhr.send('action=ai_app_onsite_get_accept_terms_of_service&terms_of_service=' + terms_of_service+'&ai_app_onsite_plugin_settings_terms_of_service_nonce='+terms_of_service_nonce);
    614 }
    615 
    616 document.addEventListener('DOMContentLoaded', function() {
    617    
    618    var button = document.getElementById('accept_ai_app_terms_of_service');
    619    button.addEventListener('click', function() {
    620         var terms_of_service = document.getElementById("terms_conditions").checked;
    621          var terms_of_service_nonce = document.getElementById("ai_app_onsite_plugin_settings_nonce_field").value;
    622         var xhr = new XMLHttpRequest(); // Initialize the XMLHttpRequest object
    623         xhr.open('POST', myPluginAjax.ajaxurl, true);
    624         xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Set the request header
    625 
    626         xhr.onreadystatechange = function () {
    627             if (xhr.readyState === 4) { // Ensure the request is complete
    628                 var messageContainer = document.getElementById('Plugin-settings-msg-container') || document.getElementById('messageContainer');
    629                 if (xhr.status === 200) {
    630                     console.log('Response Text:', xhr.responseText); // Log the raw response text
    631 
    632                     if (xhr.responseText) { // Check if the response text is not empty
    633                         try {
    634                             var response = JSON.parse(xhr.responseText);
    635                             if (messageContainer) {
    636                                 if (response.success) {
    637                                     messageContainer.innerHTML = '<span style="color: green;">' + response.data + '</span>';
    638                                 } else {
    639                                     messageContainer.innerHTML = '<span style="color: red;">Error: ' + response.error + '</span>';
    640                                 }
    641                             } else {
    642                                 console.error('messageContainer element not found');
    643                             }
    644                         } catch (e) {
    645                             console.error('Failed to parse response:', e);
    646                             if (messageContainer) {
    647                                 messageContainer.innerHTML = '<span style="color: red;">Error parsing response</span>';
    648                             } else {
    649                                 console.error('messageContainer element not found');
    650                             }
    651                         }
    652                     } else {
    653                         console.error('Empty response');
    654                         if (messageContainer) {
    655                             messageContainer.innerHTML = '<span style="color: red;">Empty response</span>';
    656                         } else {
    657                             console.error('messageContainer element not found');
    658                         }
    659                     }
    660                 } else {
    661                     console.error('Request failed with status:', xhr.status);
    662                     if (messageContainer) {
    663                         messageContainer.innerHTML = '<span style="color: red;">Error: ' + xhr.status + '</span>';
    664                     } else {
    665                         console.error('messageContainer element not found');
    666                     }
    667                 }
    668 
    669                 // Hide message after 3 seconds
    670                 setTimeout(function() {
    671                     if (messageContainer) {
    672                         messageContainer.innerHTML = '';
    673                     }
    674                 }, 3000);
    675             }
    676         };
    677 
    678        xhr.send('action=ai_app_onsite_get_accept_terms_of_service&terms_of_service=' + terms_of_service+'&ai_app_onsite_plugin_settings_terms_of_service_nonce='+terms_of_service_nonce);
    679            
    680    });
    681 
    682    
    683 
    684 });
    685 /*=============End download user stats js=================================== */
     1327          for (let key in initialState) {
     1328              if (initialState.hasOwnProperty(key)) {
     1329                  if (initialState[key] !== currentState[key]) {
     1330                      return true;
     1331                  }
     1332              }
     1333          }
     1334     
     1335          return false;
     1336      };
     1337 
     1338      const markDirty = () => {
     1339          isDirty = hasChanged();
     1340          if (typeof config.onChange === 'function') config.onChange(isDirty);
     1341      };
     1342 
     1343      const bindListeners = () => {
     1344          form.querySelectorAll('input, textarea, select').forEach(field => {
     1345              field.addEventListener('input', markDirty);
     1346              field.addEventListener('change', markDirty);
     1347          });
     1348      };
     1349 
     1350      const observer = new MutationObserver(() => {
     1351          bindListeners();
     1352          markDirty();
     1353      });
     1354 
     1355      observer.observe(form, { childList: true, subtree: true });
     1356 
     1357      captureInitialState();
     1358      bindListeners();
     1359 
     1360      return {
     1361          activate: () => {
     1362              active = true;
     1363              captureInitialState();
     1364          },
     1365          deactivate: () => {
     1366              active = false;
     1367          },
     1368          reset: () => {
     1369              captureInitialState();
     1370              isDirty = false;
     1371          },
     1372          hasUnsavedChanges: () => isDirty,
     1373          markDirty: () => markDirty
     1374      };
     1375  }
     1376 
     1377 
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-prompt-editor.js

    r3281194 r3307540  
    66var aiResponseTone = document.getElementById('ai-response-tone');
    77var egAiOutput = document.getElementById('eg-ai-output');
    8 
     8let initialPromptContent = '';
     9let appPromptFormTracker = null;
    910
    1011function onChangePromptInput() {
     
    1920    // Use TinyMCE API to get the content of the WYSIWYG editor
    2021    var promptInputValue = tinymce.get('prompt').getContent({ format: 'text' }).trim(); // 'prompt' is the editor ID
    21     console.log('Prompt Input Value:', promptInputValue); // Log to check the input value
    2222
    2323    // Target the error message container
    2424    var promptError = document.querySelector('.prompt-error'); // Ensure this selector is correct for your error display element
    25     console.log('Prompt Error Element:', promptError); // Log to verify that the error container is found
    2625
    2726    var errors = [];
     
    122121
    123122                if (response.status === '404') {
     123                    if (typeof tinymce !== "undefined" && tinymce.get('prompt')) {
     124                        tinymce.get('prompt').setContent(''); // Set content to the TinyMCE editor
     125                    }
     126                    promptInput.value = '';
    124127                    return;
    125128                }
     
    127130                // Accessing the first object in the array
    128131                var firstObject = response[0];
    129                 // console.log('firstObject =', firstObject);
    130132
    131133                // Access properties of the first object
     
    139141                if (typeof tinymce !== "undefined" && tinymce.get('prompt')) {
    140142                    tinymce.get('prompt').setContent(prompt_html); // Set content to the TinyMCE editor
     143                 
    141144                } else {
    142145                    // If TinyMCE is not initialized, set the value directly to the textarea
    143146                    promptInput.value = prompt_html;
    144147                }
     148                initialPromptContent = normalizeContent(prompt_html);
    145149
    146150                if (ai_response_style) {
     
    176180//Start save model settings JS
    177181function ai_app_onsite_save_prompt_editor() {
    178 
    179     // console.log('v1');
    180182
    181183    //check Prompt value
     
    233235    }
    234236
    235     // console.log('promptInputValue = ', promptInputValue);
    236 
    237237    var aiResponseStyleValue = aiResponseStyle ? aiResponseStyle.value.trim() : '';
    238238    var aiResponseToneValue = aiResponseTone ? aiResponseTone.value.trim() : '';
     
    252252}
    253253
    254 
    255 // Wait for the DOM to fully load
    256254document.addEventListener("DOMContentLoaded", function () {
    257 
    258     // Make sure TinyMCE is available
    259255    if (typeof tinymce !== "undefined") {
    260         // Initialize the TinyMCE editor
    261         tinymce.init({
    262             selector: '#prompt',  // Target the wp_editor's textarea by editor_id
    263             setup: function (editor) {
    264                 // Listen for changes in the editor content
    265                 editor.on('change', function () {
    266                     // Call your custom JavaScript function
    267                     onChangePromptInput();
     256        tinymce.on('AddEditor', function (e) {
     257            if (e.editor.id === 'prompt') {
     258                const editor = e.editor;
     259
     260                editor.on('init', function () {
     261                    // Setup form tracker
     262                    appPromptFormTracker = trackFormChanges('#prompt-editor');
     263                    window.FormChangeRegistry.register('app_prompt_settings', appPromptFormTracker, 'prompt-editor');
     264
     265                    document.getElementById('ai_app_onsite_save_prompt_editor').addEventListener('click', function (e) {
     266                        e.preventDefault();
     267
     268                        appPromptFormTracker.reset();
     269                        ai_app_onsite_save_prompt_editor();
     270                    });
    268271                });
     272
     273                editor.on('input change undo redo', function () {
     274                    const currentContent = normalizeContent(editor.getContent({ format: 'html' }));
     275                    if (currentContent !== initialPromptContent) {
     276                        document.querySelector('textarea[name="editor_prompt_aiapp"]').value = editor.getContent(); // store actual HTML or raw value
     277                        appPromptFormTracker.markDirty();
     278                    } else {
     279                        appPromptFormTracker.reset();
     280                    }
     281                });
    269282            }
    270283        });
     284    }else {
     285        if (promptError) {
     286            promptError.innerHTML = 'For best performance please install the <a href="https://wordpress.org/plugins/classic-editor/" target="_blank">Classic Editor</a> WP plugin.';
     287        }
    271288    }
    272289});
    273290
    274 
    275 
     291// Normalization function
     292function normalizeContent(content) {
     293    return content.replace(/\u00a0/g, ' ') // &nbsp;
     294                  .replace(/[\r\n]+/g, ' ') // newlines
     295                  .replace(/\s+/g, ' ')     // multiple spaces to one
     296                  .trim();
     297}
     298
     299
     300
     301
     302
     303
     304
     305
     306
     307
     308
  • ai-app-onsite/trunk/assets/js/ai-app-onsite-reset-localstorage.js

    r3250856 r3307540  
    22    document.querySelectorAll('.deactivate a').forEach(function(element) {
    33        element.addEventListener('click', function(e) {
    4             var pluginSlug = 'ai-app-onsite/ai-app-onsite.php'; // Replace with your plugin slug
    54            var href = this.getAttribute('href');
    65              e.preventDefault(); // Prevent the default deactivation action
     
    98                  localStorage.setItem("settings", "{}"); // or use a default settings JSON object
    109                  localStorage.setItem("app_Title_gutenberg_block", "");
    11                   console.log("LocalStorage keys have been reset");
    1210                }
    13                     window.location.href = href; // Proceed with the deactivation
    14                
    15            
     11                window.location.href = href; // Proceed with the deactivation
    1612        });
    1713    });
  • ai-app-onsite/trunk/handler/ai-app-onsite-app-preview.php

    r3281194 r3307540  
    2222            global $wpdb;
    2323            $table_appropriate = $wpdb->prefix . 'ai_app_onsite_app_properties';
    24             $appropriates = $wpdb->get_results("SELECT * FROM $table_appropriate", ARRAY_A);
     24            $appropriates = $wpdb->get_results("SELECT * FROM $table_appropriate WHERE app_id = 1", ARRAY_A);
    2525            if (empty($appropriates)) {
    26                 // error_log('No data found in app properties');
     26                error_log('No data found in app properties');
    2727                return;
    2828            }
     29
    2930            $is_preview = isset($_POST['is_preview_button']) && $_POST['is_preview_button'] === 'true';
    3031
     
    3233            $appropriates[0]['font_color']  = ($is_preview && isset($_POST['font_color'])) ? $_POST['font_color'] : $appropriates[0]['font_color'];
    3334            $appropriates[0]['bg_color']    = ($is_preview && isset($_POST['background_color'])) ? $_POST['background_color'] : $appropriates[0]['bg_color'];
    34 
    35             $appropriates[0]['app_corner']    = ($is_preview && isset($_POST['app_corner'])) ? $_POST['app_corner'] : $appropriates[0]['app_corner'];
    36             $appropriates[0]['app_height']    = ($is_preview && isset($_POST['app_height'])) ? $_POST['app_height'] : $appropriates[0]['app_height'];
    37 
    38             $titleColors = json_decode($appropriates[0]['title_color'], true);
    39             $fontColors = json_decode($appropriates[0]['font_color'], true);
    40             $bgColors = json_decode($appropriates[0]['bg_color'], true);
    41             $appCorner = $appropriates[0]['app_corner'];
    42             $appHeight = $appropriates[0]['app_height'];
    43 
    44 
     35            $appropriates[0]['app_corner']  = ($is_preview && isset($_POST['app_corner'])) ? $_POST['app_corner'] : $appropriates[0]['app_corner'];
     36            $appropriates[0]['app_height']  = ($is_preview && isset($_POST['app_height'])) ? $_POST['app_height'] : $appropriates[0]['app_height'];
     37
     38            $titleColors = isset($appropriates[0]['title_color']) ? json_decode($appropriates[0]['title_color'], true) : null;
     39            $fontColors  = isset($appropriates[0]['font_color']) ? json_decode($appropriates[0]['font_color'], true) : null;
     40            $bgColors    = isset($appropriates[0]['bg_color']) ?  json_decode($appropriates[0]['bg_color'], true) : null;
     41            $appCorner   = $appropriates[0]['app_corner'];
     42            $appHeight   = $appropriates[0]['app_height'];
    4543
    4644            $title_color_selected = null;
    47             $font_color_selected = null;
    48             $bg_color_selected = null;
    49             $app_corner_selected = null;
    50             $app_height_selected = null;
     45            $font_color_selected  = null;
     46            $bg_color_selected    = null;
     47            $app_corner_selected  = null;
     48            $app_height_selected  = null;
    5149
    5250            if(!is_null($titleColors)) {
     
    9492
    9593            $this->appProperty = [
    96                 'app_name' => $appropriates[0]['app_name'],
    97                 'app_logo' => $appropriates[0]['app_logo'],
    98                 'app_disclaimer' => $appropriates[0]['app_disclaimer'],
    99                 'app_description' => $appropriates[0]['app_description'],
    100                 'submit_button_txt' => $appropriates[0]['submit_button_txt'],
    101                 'title_color' => 'style="color:' . $title_color . ';"',
    102                 'font_color' => 'style="color:' . $font_color . ';"',
    103                 'small_font_color' => 'style="color:' . $font_color . '; opacity:0.5;"',
     94                'app_name'              => $appropriates[0]['app_name'],
     95                'app_logo'              => $appropriates[0]['app_logo'],
     96                'app_disclaimer'        => $appropriates[0]['app_disclaimer'],
     97                'app_description'       => $appropriates[0]['app_description'],
     98                'submit_button_txt'     => $appropriates[0]['submit_button_txt'],
     99                'title_color'           => 'style="color:' . $title_color . ';"',
     100                'font_color'            => 'style="color:' . $font_color . ';"',
     101                'small_font_color'      => 'style="color:' . $font_color . '; opacity:0.5;"',
    104102                'app_description_color' => 'style="color:' . $font_color . '; opacity:0.5; text-align: center; font-size: 20px;"',
    105                 'background_color' => $background_color,
    106                 'btn_color' => 'style="background-color:' . $title_color . ';"'
     103                'background_color'      => $background_color,
     104                'btn_color'             => 'style="background-color:' . $title_color . ';"',
     105                'short_code_genrated'   => $appropriates[0]['short_code_genrated']
    107106            ];
    108107
     
    136135                    break;
    137136            }
    138 
    139137
    140138            //New Logic
     
    144142            }
    145143            if ($appName !== "aiapp") {
    146 
    147144                $shortCodeGenrated = $appropriates[0]['short_code_genrated'];
    148145
     
    172169        }
    173170
     171        private function ai_app_onsite_json_error_404($message) {
     172            wp_send_json(array(
     173                'status'  => '404',
     174                'message' => $message
     175            ));
     176        }
     177
    174178        // AJAX callback function
    175179        public function ai_app_onsite_create_app_preview_form()
     
    178182            $table_name_settings = $wpdb->prefix . 'ai_app_onsite_field_selector_settings';
    179183            $table_name_app_properties = $wpdb->prefix . 'ai_app_onsite_app_properties';
    180 
    181             // if $this->appProperty is not initialized return
    182             if (empty($this->appProperty)) {
    183                 wp_send_json_error('App properties are not initialized.');
    184                 return;
    185             }
    186 
    187 
    188             // Check if the table exists
    189             if ($wpdb->get_var("SHOW TABLES LIKE '$table_name_settings'") != $table_name_settings) {
    190                 $results = array('status' => '404', 'message' => 'No App found');
    191                 wp_send_json($results);
    192             }
    193 
    194             // Check if the table has rows
    195             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
    196             if ($rows_count == 0) {
    197                 $results = array('status' => '404', 'message' => 'No rows found in the table.');
    198                 wp_send_json($results);
    199             }
     184            $table_name_model_settings = $wpdb->prefix . 'ai_app_onsite_model_settings';
     185
     186            // Check if the table ai_app_onsite_field_selector_settings exists
     187            if ($wpdb->get_var("SHOW TABLES LIKE '$table_name_settings'") != $table_name_settings || $wpdb->get_var("SHOW TABLES LIKE '$table_name_model_settings'") != $table_name_model_settings) {
     188                $this->ai_app_onsite_json_error_404('No App found');
     189            }
     190
     191            // Check if the table ai_app_onsite_field_selector_settings has rows
     192            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
     193            $model_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_model_settings WHERE app_id = 1");
     194       
     195            if ($rows_count == 0 || $model_count == 0 ||  empty($this->appProperty)) {
     196                $this->ai_app_onsite_json_error_404('It looks like some required settings are missing. Please ensure that "App Details," "Model Settings," and "Form Fields" are configured before previewing the form.');
     197            }
     198           
     199           
    200200
    201201            // Fetch the data
    202             $results = $wpdb->get_results("SELECT * FROM $table_name_settings ORDER BY field_position ASC", ARRAY_A);
     202            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1 ORDER BY field_position ASC", ARRAY_A);
    203203
    204204            $table_appropriate = $wpdb->prefix . 'ai_app_onsite_app_properties';
    205             $appropriates = $wpdb->get_results("SELECT * FROM $table_appropriate", ARRAY_A);
     205            $appropriates = $wpdb->get_results("SELECT * FROM $table_appropriate WHERE app_id = 1", ARRAY_A);
     206            if(
     207                empty($appropriates[0]['title_color']) ||
     208                empty($appropriates[0]['font_color']) ||
     209                empty($appropriates[0]['bg_color']) ||
     210                empty($appropriates[0]['app_corner']) ||
     211                empty($appropriates[0]['app_height']))
     212            {
     213                $this->ai_app_onsite_json_error_404('It looks like some required settings are missing. Please ensure that "App Details" are configured before previewing the form.');
     214            }
    206215            $app_logo = isset($appropriates[0]['app_logo']) && !empty($appropriates[0]['app_logo'])
    207216                ? $appropriates[0]['app_logo']
     
    286295
    287296            $formHTML .= '<div class="ai-app-preview-powered_by_note-wrapper">
    288                 <p class="powered_by" style="opacity:0.5;text-align: right;margin: 0; color:#159AD7;"><span class="disclaimer-heading"><a href="https://www.aiapponsite.com/" target="_blank">Built with AIappOnsite</a></span>
     297                <p class="powered_by" style="opacity:0.5;text-align: right;margin: 0; color:#159AD7;"><span class="disclaimer-heading"><a href="https://www.aiapponsite.com/" class="open-new-tab">Built with AIappOnsite</a></span>
    289298            </div>';
    290299
     
    333342
    334343            $table_name = $wpdb->prefix . 'ai_app_onsite_app_properties';
    335             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name");
     344            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name WHERE app_id = 1");
    336345
    337346            if ($existing_settings) {
     
    360369        }
    361370
    362 
    363 
    364 
    365371        // Define shortcode function
    366372        public function ai_app_onsite_create_openAi_app_form_shortcode(): string {
    367373            global $wpdb;
    368374            $table_appropriate = $wpdb->prefix . 'ai_app_onsite_app_properties';
    369             $appropriates = $wpdb->get_results("SELECT * FROM $table_appropriate", ARRAY_A);
     375            $appropriates = $wpdb->get_results("SELECT * FROM $table_appropriate WHERE app_id = 1", ARRAY_A);
    370376
    371377            // Make AJAX call to retrieve form HTML
     
    411417                    jQuery(document).ready(function($) {
    412418
    413                         $(".ai-app-feedback-section .like i").on("click", function() {
    414                             var feedback = $(this).hasClass("fa-thumbs-up") ? "thumbs_up" : "thumbs_down";
     419                        $(".ai-app-feedback-section .like i, .ai-app-feedback-section .dislike i").on("click", function() {
     420                           var feedback = $(this).hasClass("fa-thumbs-up") ? "thumbs_up" : "thumbs_down";
    415421                           var response_id = $("#ai_response_id").val();
    416                             console.log(response_id);
    417422                            $.ajax({
    418423                                url: "' . admin_url('admin-ajax.php') . '",
     
    456461
    457462            // Check if the table has rows
    458             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     463            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
    459464            if ($rows_count == 0) {
    460465                //die('No rows found in the table.');
     
    465470
    466471            // Fetch the data
    467             $results = $wpdb->get_results("SELECT * FROM $table_name_settings", ARRAY_A);
     472            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    468473
    469474            $generateURLForID  = $results[0]['app_logo'];
  • ai-app-onsite/trunk/handler/ai-app-onsite-app-properties.php

    r3281194 r3307540  
    149149                        'app_height' => $app_height
    150150                    ),
    151                     array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')
     151                    array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')
    152152                );
    153153
     
    177177
    178178            // Check if the table has rows
    179             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     179            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
     180     
    180181            if ($rows_count == 0) {
    181182                //die('No rows found in the table.');
    182                 $results = array('status' => '404', 'message' => 'No rows found in the table.');
     183                $results = array('status' => '404', 'message' => 'App not found. Please create an app first.');
    183184
    184185                wp_send_json($results);
     
    186187
    187188            // Fetch the data
    188             $results = $wpdb->get_results("SELECT * FROM $table_name_settings", ARRAY_A);
     189            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    189190
    190191            $generateURLForID  = $results[0]['app_logo'];
     
    253254            $table_name_settings = $wpdb->prefix . 'ai_app_onsite_app_properties';
    254255
    255             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     256            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    256257            if ($existing_settings) {
    257258
     
    298299            }
    299300
    300             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     301            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    301302            $file_url = $existing_settings->app_logo;
    302303            // Check if the database operation was successful
     
    323324            }
    324325
    325             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     326            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    326327            if ($existing_settings) {
    327328
  • ai-app-onsite/trunk/handler/ai-app-onsite-field-selector.php

    r3250856 r3307540  
    4747            $form_data_array = json_decode($form_data_array, true);
    4848
    49 
    50             // echo '<pre>'; print_r($form_data_array); echo '</pre>';
    51 
    5249            // Table name for field selector settings
    5350            $table_name_settings = $wpdb->prefix . 'ai_app_onsite_field_selector_settings';
    5451
    55             $max_field_option = $wpdb->get_var("SELECT MAX(field_position) FROM $table_name_settings");
    56 
    57 
    58 
     52            $max_field_option = $wpdb->get_var("SELECT MAX(field_position) FROM $table_name_settings WHERE app_id = 1");
    5953
    6054            // Iterate through form data and insert/update settings
     
    175169        public function ai_app_onsite_read_field_selector_callback()
    176170        {
    177 
    178171            global $wpdb;
    179172
     
    181174
    182175            // Fetch data from the database
    183             $results = $wpdb->get_results("SELECT * FROM $table_name ORDER BY field_position ASC", ARRAY_A);
     176            $results = $wpdb->get_results("SELECT * FROM $table_name WHERE app_id = 1 ORDER BY field_position ASC", ARRAY_A);
    184177
    185178
     
    190183            $ctr = 0;
    191184
    192             $app_width = $results[0]['app_width'];
     185            $app_width = $results[0]['app_width'] ?? '70';
    193186
    194187
     
    266259            // Return the HTML content
    267260            $arrayName = array('app_width' => $app_width, 'html_content' => $html_content, 'count' => $ctr);
    268             //echo $html_content;
    269261
    270262            wp_send_json($arrayName);
     
    315307        public function ai_app_onsite_drag_data_save_database_callback()
    316308        {
    317 
    318 
    319309            if (
    320310                ! isset($_POST['ai_app_onsite_fields_selector_order_nonce']) ||
     
    324314            }
    325315
    326 
    327 
    328316            global $wpdb;
    329317            $table_name = $wpdb->prefix . 'ai_app_onsite_field_selector_settings';
     
    336324            // Iterate through the form data array
    337325            foreach ($form_data_array as $key => $value) {
    338                 // Prepare the data to update
    339                 /*  $update_data = array(
    340                'field_position' => $value['fieldOrder'],
    341                'field_uniqueid' => $value['fieldUniqueId']
    342            );
    343 
    344            // Print the data to be updated (for debugging purposes)
    345            print_r($update_data);*/
     326         
    346327                $field_position =  $value['fieldOrder'] / 3;
    347328                // Uncomment the update query to actually update the data
  • ai-app-onsite/trunk/handler/ai-app-onsite-model-settings.php

    r3272415 r3307540  
    5151            $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings';
    5252
    53             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     53            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    5454
    5555            if ($existing_settings) {
     
    6969                    array('%s', '%f', '%f', '%f', '%d', '%f'),
    7070                );
    71                 $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     71                $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    7272
    7373                // Check if the database operation was successful
     
    9595                );
    9696            }
    97             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     97            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    9898
    9999            // Check if the database operation was successful
     
    119119
    120120            // Check if the table has rows
    121             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     121            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
    122122            if ($rows_count == 0) {
    123123                $results = array('status' => '404',  'message' => 'No rows found in the table.');
     
    130130
    131131            // Fetch the data
    132             $results = $wpdb->get_results("SELECT * FROM $table_name_settings", ARRAY_A);
     132            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    133133
    134134            // Return the data
     
    163163
    164164            // Fetch the data
    165             $results = $wpdb->get_results("SELECT openai_key, key_status FROM $table_name_settings", ARRAY_A);
     165            $results = $wpdb->get_results("SELECT openai_key, key_status FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    166166
    167167            // Return the data
     
    197197            $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings';
    198198
    199             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     199            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    200200
    201201            if ($existing_settings) {
  • ai-app-onsite/trunk/handler/ai-app-onsite-openAi-api.php

    r3281194 r3307540  
    3434
    3535
    36             $model_settings = $wpdb->get_results("SELECT * FROM $table_name_model_settings", ARRAY_A);
    37 
    38             $plugin_settings = $wpdb->get_results("SELECT * FROM $table_name_plugin_settings", ARRAY_A);
     36            $model_settings = $wpdb->get_results("SELECT * FROM $table_name_model_settings WHERE app_id = 1", ARRAY_A);
     37
     38            $plugin_settings = $wpdb->get_results("SELECT * FROM $table_name_plugin_settings WHERE app_id = 1", ARRAY_A);
    3939            $formData = $_POST;
    4040            $field_names = array_keys($formData);
    4141            $field_values = array_values($formData);
    4242            $pairs = array_combine($field_names, $field_values);
    43             $banned_words = unserialize($plugin_settings[0]['banned_words']);
    44             $app_email = $plugin_settings[0]['app_email'];
     43            $banned_words = (!empty($plugin_settings) && isset($plugin_settings[0]['banned_words']) && !is_null($plugin_settings[0]['banned_words']))
     44            ? unserialize($plugin_settings[0]['banned_words'])
     45            : [];
     46            $app_email = isset($plugin_settings[0]['app_email']) ? $plugin_settings[0]['app_email'] : null;
    4547
    4648            function containsBannedWordPart($value, $banned_word)
     
    5860
    5961            // Iterate through field values and check against banned words
    60             foreach ($field_values as $value) {
    61                 foreach ($banned_words as $banned_word) {
    62                     if (containsBannedWordPart($value, $banned_word)) {
    63                         wp_send_json_error("Banned word included in: " . $value);
    64                         break;
     62            if (!empty($banned_words)) {
     63                foreach ($field_values as $value) {
     64                    foreach ($banned_words as $banned_word) {
     65                        if (containsBannedWordPart($value, $banned_word)) {
     66                            wp_send_json_error("Banned word included in: " . $value);
     67                            break 2;
     68                        }
    6569                    }
    6670                }
    6771            }
    6872
    69             $field_selector_settings = $wpdb->get_results("SELECT `field_label` FROM $table_name_field_selector", ARRAY_A);
    70 
     73
     74            $field_selector_settings = $wpdb->get_results("SELECT `field_label` FROM $table_name_field_selector WHERE app_id = 1", ARRAY_A);
     75           
    7176            $fieldLabels = array_column($field_selector_settings, 'field_label');
    7277            // Process each element of the array
     
    163168
    164169            $replace_array = array_combine($formatted_field_names, $field_values);
    165 
    166             $prompt_results = $wpdb->get_results("SELECT * FROM $table_name_prompt_editor", ARRAY_A);
    167 
     170           
     171
     172            $prompt_results = $wpdb->get_results("SELECT * FROM $table_name_prompt_editor WHERE app_id = 1", ARRAY_A);
     173           
    168174            if (empty($prompt_results)) {
    169175                wp_send_json_error('No prompt results found');
  • ai-app-onsite/trunk/handler/ai-app-onsite-plugin-settings.php

    r3250856 r3307540  
    1313            add_action( 'wp_ajax_nopriv_ai_app_onsite_handle_csv_upload', array( $this, 'ai_app_onsite_handle_csv_upload_callback' ) );
    1414
    15               add_action( 'wp_ajax_ai_app_onsite_get_accept_terms_of_service', array( $this, 'ai_app_onsite_get_accept_terms_of_service_callback' ) );
     15            add_action( 'wp_ajax_ai_app_onsite_get_accept_terms_of_service', array( $this, 'ai_app_onsite_get_accept_terms_of_service_callback' ) );
    1616            add_action( 'wp_ajax_nopriv_ai_app_onsite_get_accept_terms_of_service', array( $this, 'ai_app_onsite_get_accept_terms_of_service_callback' ) );
    1717
     
    1919        }
    2020
    21         // Public method to handle AJAX callback
    22 
    2321        public function ai_app_onsite_save_plugin_settings_callback() {
    24 
    25 
    26 
    2722            global $wpdb; // Access the WordPress database object
    2823            if (
     
    3328            }
    3429
    35             // Handle AJAX request
    36            
    3730            // Check if settings already exist in the database
    3831            $table_name_settings = $wpdb->prefix . 'ai_app_onsite_plugin_settings';
     
    4033            // Extract individual settings values
    4134            $agree_terms = isset($_POST['terms_of_service'] ) && $_POST['terms_of_service']  == true ? 1 : 0;
    42             // $app_selector = isset($_POST['app_selector'] ) ? sanitize_text_field($_POST['app_selector'] ) : '';
    43             // $app_email = isset($_POST['app_email'] ) ? sanitize_text_field($_POST['app_email'] ) : '';
    44             // $banned_words = isset($_POST['banned_words'] ) ? serialize($_POST['banned_words'] ) : '';
    45          
    4635            $app_selector = isset( $_POST['app_selector'] ) ? sanitize_text_field( wp_unslash( $_POST['app_selector'] ) ) : '';
    4736            $app_email = isset($_POST['app_email'] ) ? sanitize_text_field(wp_unslash($_POST['app_email']) ) : '';
    4837            $banned_words = isset( $_POST['banned_words'] ) ? serialize( array_map( 'sanitize_text_field', wp_unslash( $_POST['banned_words'] ) ) ) : '';
    49 
    50 
    51             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     38           
     39            if ($app_selector == '') {
     40                $results = array('status' => '404', 'error' => 'App Name is required.');
     41                wp_send_json($results);
     42                exit;
     43            }
     44
     45            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    5246
    5347            // If settings already exist, update the row; otherwise, insert a new row
    5448            if ($existing_settings) {
    55                 // Update existing row
    56                // Define the update data array
    5749                $update_data = array(
    5850                    'agree_terms' => $agree_terms,
     
    8476                    wp_send_json_error('Error updating data: ' . $wpdb->last_error);
    8577                } else {
     78                    $table_app_properties = $wpdb->prefix . 'ai_app_onsite_app_properties';
     79
     80                    $existing_app = $wpdb->get_row("SELECT * FROM $table_app_properties WHERE app_id = 1");
     81       
     82                    if ($existing_app) {
     83                        // Update the app name if it already exists
     84                        $wpdb->update(
     85                            $table_app_properties,
     86                            array(
     87                                'app_name' => $app_selector,
     88                            ),
     89                            array('app_id' => $existing_app->app_id),
     90                            array('%s'),
     91                            array('%d')
     92                        );
     93       
     94                        if ($wpdb->last_error) {
     95                            wp_send_json_error('Error updating app name: ' . $wpdb->last_error);
     96                        }
     97                    } else {
     98                        // Insert new app name if it doesn't exist
     99                        $wpdb->insert(
     100                            $table_app_properties,
     101                            array(
     102                                'app_id' => 1,
     103                                'app_name' => $app_selector,
     104                                'submit_button_txt' => 'Submit',
     105                            ),
     106                            array('%d', '%s', '%s')
     107                        );
     108       
     109                        if ($wpdb->last_error) {
     110                            wp_send_json_error('Error saving app name: ' . $wpdb->last_error);
     111                        }
     112                    }
    86113                    // Return success message for update
    87114                    wp_send_json_success('Settings updated successfully.');
     
    107134                } else {
    108135                    // Return success message for insert
     136                    $table_app_properties = $wpdb->prefix . 'ai_app_onsite_app_properties';
     137
     138                    // Insert new app name into the app properties table
     139                    $wpdb->insert(
     140                        $table_app_properties,
     141                        array(
     142                            'app_id' => 1,
     143                            'app_name' => $app_selector,
     144                            'submit_button_txt' => 'Submit',
     145                        ),
     146                        array('%d', '%s', '%s')
     147                    );
     148
     149                    if ($wpdb->last_error) {
     150                        wp_send_json_error('Error saving app name: ' . $wpdb->last_error);
     151                    }
    109152                    wp_send_json_success('Settings saved successfully.');
    110153                }
    111154            }
     155           
    112156        }
    113157
     
    124168
    125169            // Check if the table has rows
    126             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     170            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
    127171            if ($rows_count == 0) {
    128                
    129172               $results = array('status' => '404',  'message' => 'No rows found in the table.');
    130173
     
    135178            }
    136179
    137 
    138 
    139              $getAppName = $wpdb->get_results("SELECT * FROM $table_app_name", ARRAY_A);
    140 
    141             $appName = $getAppName[0]['app_name'];
     180            $getAppName = $wpdb->get_results("SELECT * FROM $table_app_name WHERE app_id = 1", ARRAY_A);
     181
     182            $appName = isset($getAppName[0]['app_name_show']) && !empty($getAppName[0]['app_name_show'])
     183                ? $getAppName[0]['app_name_show']
     184                : (isset($getAppName[0]['app_name']) ? $getAppName[0]['app_name'] : '');
    142185           
    143186            // Fetch the data
    144             $results = $wpdb->get_results("SELECT * FROM $table_name_settings", ARRAY_A);
    145 
     187            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
     188
     189            //  Create a mapping of app IDs to app names
     190            $appNameMapping = [];
    146191            // Unserialize 'banned_words_csv' before sending response
    147192                foreach ($results as &$row) {
    148                    if (!empty($appName)) {
    149                            $row['appName'] = $appName;
    150                        }
    151                     $unserializedDataCsv = unserialize($row['banned_words_csv']);
     193                    if (!empty($appName)) {
     194                        $row['appName'] = $appName;
     195                    }
     196                    $banned_words_raw = $row['banned_words_csv'] ?? '';
     197
     198                    if (is_string($banned_words_raw) && $banned_words_raw !== '' && is_serialized($banned_words_raw)) {
     199                        $unserializedDataCsv = unserialize($banned_words_raw);
     200                    } else {
     201                        $unserializedDataCsv = [];
     202                    }
    152203                    $unserializedData = unserialize($row['banned_words']);
    153204                        if ($unserializedDataCsv !== false) {
     
    224275               }
    225276
    226                    $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     277                   $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    227278                   if ($existing_settings) {
    228279                       if (!empty($serializedData)) {
     
    278329               
    279330            $terms_of_service = isset($_POST['terms_of_service']) ? intval($_POST['terms_of_service']) : 0;
    280              $checked = ($_POST['terms_of_service'] == true) ? 1 : 0;
    281 
    282 
    283 
    284 
    285 
    286             $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings");
     331            $checked = ($_POST['terms_of_service'] == true) ? 1 : 0;
     332
     333            $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1");
    287334
    288335            if ($existing_settings) {
  • ai-app-onsite/trunk/handler/ai-app-onsite-prompt-editor.php

    r3272415 r3307540  
    116116
    117117            // Check if the table has rows
    118             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     118            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
    119119            if ($rows_count == 0) {
    120 
    121120                $results = array('status' => '404',  'message' => 'No rows found in the table.');
    122121
     
    128127
    129128            // Fetch the data
    130             $results = $wpdb->get_results("SELECT field_tag FROM $table_name_settings", ARRAY_A);
     129            $results = $wpdb->get_results("SELECT field_tag FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    131130
    132131            // Return the data
     
    149148
    150149            // Check if the table has rows
    151             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     150            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
    152151            if ($rows_count == 0) {
    153152                $results = array('status' => '404',  'message' => 'No rows found in the table.');
     
    160159
    161160            // Fetch the data
    162             $results = $wpdb->get_results("SELECT * FROM $table_name_settings", ARRAY_A);
     161            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    163162
    164163            // Return the data
     
    180179
    181180            // Check if the table has rows
    182             $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings");
     181            $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1");
    183182            if ($rows_count == 0) {
    184183
     
    192191
    193192            // Fetch the data
    194             $results = $wpdb->get_results("SELECT * FROM $table_name_settings", ARRAY_A);
     193            $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A);
    195194
    196195            // Return the data
  • ai-app-onsite/trunk/handler/ai-app-onsite-user-stats.php

    r3281194 r3307540  
    104104            $table_name = $wpdb->prefix . 'ai_app_onsite_user_statistics';
    105105            $query = "SELECT * FROM $table_name";
    106             $user_stats = $wpdb->get_results("SELECT * FROM $table_name WHERE MONTH(updated_at) = %d", $month);
     106            $user_stats = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE app_id = 1 AND MONTH(updated_at) = %d", $month));
    107107           
    108108            // Check if there are any user statistics
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-admin.php

    r3272415 r3307540  
    3131            <div class="wrap ai-app-onsite-wrap">
    3232                <div class=" ai-app-onsite-top-bar d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white box-shadow">
    33                     <h5 class="my-0 mr-md-auto font-weight-normal logo-bg-fixed">
    34 
    35                     </h5>
     33                    <h5 class="my-0 mr-md-auto font-weight-normal logo-bg-fixed"></h5>
     34                    <?php if (ai_app_onsite_is_premium_disabled()) : ?>
     35                        <h5 class="gold-gradient" id="gold_gradient">PREMIUM</h5>
     36                    <?php endif; ?>
    3637                </div>
    3738                <div class="ai-app-onsite-tab-content-box shadow-sm rounded">
    3839                    <!-- Tab navigation -->
    3940                    <h2 class="nav-tab-wrapper">
    40                         <a href="#" class="nav-tab nav-tab-active" id="plugin-settings-tab" onclick="handleTabClick('plugin-settings-tab')">
     41                        <a href="#" class="nav-tab nav-tab-active plugin-tab" id="plugin-settings-tab" data-form-tab="app-settings" data-tab-id="plugin-settings-tab">
    4142                            <div class="icon">
    4243                                <div class="assets-settings-grey-icon"></div>
    4344                                <div class="assets-settings-blue-icon"></div>
    4445                            </div>
    45                             Plugin Settings
    46                         </a>
    47                         <a href="#" class="nav-tab" id="app-properties-tab" onclick="handleTabClick('app-properties-tab')">
     46                            App Settings
     47                        </a>
     48                        <a href="#" class="nav-tab plugin-tab" id="app-properties-tab"  data-form-tab="app-details"  data-tab-id="app-properties-tab">
    4849                            <div class="icon">
    4950                                <div class="assets-clapperboard-grey-icon"></div>
    5051                                <div class="assets-clapperboard-blue-icon"></div>
    5152                            </div>
    52                             App Properties
    53                         </a>
    54                         <a href="#" class="nav-tab" id="model-settings-tab" onclick="handleTabClick('model-settings-tab')">
     53                            App Details
     54                        </a>
     55                        <a href="#" class="nav-tab plugin-tab" id="model-settings-tab" data-form-tab="model-settings"  data-tab-id="model-settings-tab">
    5556                            <div class="icon">
    5657                                <div class="assets-disc-grey-icon"></div>
     
    5960                            Model Settings
    6061                        </a>
    61                         <a href="#" class="nav-tab" id="field-selector-tab" onclick="handleTabClick('field-selector-tab')">
     62                        <a href="#" class="nav-tab plugin-tab" id="field-selector-tab" data-form-tab="field-selector" data-tab-id="field-selector-tab">
    6263                            <div class="icon">
    6364                                <div class="assets-chevron-grey-icon"></div>
     
    6667                            Field Selector
    6768                        </a>
    68                         <a href="#" class="nav-tab" id="prompt-editor-tab" onclick="handleTabClick('prompt-editor-tab')">
     69                        <a href="#" class="nav-tab plugin-tab" id="prompt-editor-tab" data-form-tab="prompt-editor" data-tab-id="prompt-editor-tab">
    6970                            <div class="icon">
    7071                                <div class="assets-promt-icon-grey-icon"></div>
     
    7374                            Prompt Editor
    7475                        </a>
    75                         <a href="#" class="nav-tab" id="app-preview-tab" onclick="handleTabClick('app-preview-tab')">
     76                        <a href="#" class="nav-tab plugin-tab" id="app-preview-tab" data-form-tab="app-preview" data-tab-id="app-preview-tab">
    7677                            <div class="icon">
    7778                                <div class="assets-preview-icon-grey-icon"></div>
     
    285286            </div>
    286287
     288            <div class="modal fade" id="unsavedChangesModal" tabindex="-1" aria-labelledby="unsavedChangesModalLabel" aria-hidden="true">
     289                <div class="modal-dialog modal-dialog-centered">
     290                    <div class="modal-content text-center">
     291                    <div class="modal-header">
     292                        <h5 class="modal-title" id="unsavedChangesModalLabel">Unsaved Changes</h5>
     293                        <button type="button" class="btn-close dismiss-unsaved-modal"></button>
     294                    </div>
     295                    <div class="modal-body">
     296                        You have unsaved changes. Please save them before navigating away or refreshing.
     297                    </div>
     298                    <div class="modal-footer justify-content-center">
     299                        <button type="button" class="btn btn-primary" aria-label="Close" data-bs-dismiss="modal">Got It</button>
     300                        <!-- class="btn btn-primary dismiss-unsaved-modal" -->
     301                    </div>
     302                    </div>
     303                </div>
     304            </div>
     305
    287306                  <!-- Modal -->
    288       <div class="modal fade bd-example-modal-sm plugin-setting" id="terms_conditions_modal" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
    289         <div class="modal-dialog modal-dialog-centered" role="document">
    290           <div class="modal-content">
    291             <div class="modal-header">
    292               <h5 class="fs-bold-16" id="terms_conditions_modal_label">Terms of Services</h5>
    293               <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
     307            <div class="modal fade bd-example-modal-sm plugin-setting" id="terms_conditions_modal" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
     308                <div class="modal-dialog modal-dialog-centered" role="document">
     309                <div class="modal-content">
     310                    <div class="modal-header">
     311                    <h5 class="fs-bold-16" id="terms_conditions_modal_label">Terms of Services</h5>
     312                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
     313                    </div>
     314                    <div class="modal-body mb-1">
     315                    <div id="terms-conditions-modal-pdf">
     316                        <p class="fs-regular-14 neutral-black-fs-100 mb-3 text-center">Terms of Services</p>
     317                        <p class="fs-regular-14 neutral-black-fs-90">Please read these terms carefully before using our service.</p>
     318                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">1. User Responsibility</p>
     319                        <p class="fs-regular-12 neutral-black-fs-70">If you reside in the European Economic Area, Switzerland, the UK, California or some other locations, you may be subject to special conditions and laws that govern the use of AI. These and other locales also have privacy and data protection rules and laws that may also dictate how you can leverage tools like AIappOnsite.</p>
     320                        <p class="fs-regular-12 neutral-black-fs-70">It is important that you abide by all such applicable laws. Furthermore, you may not use the Service in a way that infringes, misappropriates or violates anyone’s rights. You may also not use the Service in a way that endangers or harms, or intends to do the same, to end users of applications built with the Service.</p>
     321
     322                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">2. Using AI Services</p>
     323                        <p class="fs-regular-12 neutral-black-fs-70">The Service requires that the site owner or their designee provide an API key which the Service will use tointegrate with an AI model provider. All AI functionality of the Service depends on one or more API keys, whichis provided by the various AI model vendors. An account must be opened with one of these vendors in order to get an API key.</p>
     324                        <p class="fs-regular-12 neutral-black-fs-70">These AI model providers are separate companies and unaffiliated with the Service, and have their own terms and conditions. Read them carefully. The API keys and their use are bound by the terms and conditions of the AI model provider and strictly controlled by the site owner or their designee (not AIappOnsite).</p>
     325
     326                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">3. Limitation of Liability</p>
     327                        <p class="fs-regular-12 neutral-black-fs-70">Given that the site owner fully controls the use, purpose and output of any application built with it, AIappOnsite bears no responsibility or liability for how it is used, or to any consequences of its use by site owners or their site visitors.</p>
     328                        <p class="fs-regular-12 neutral-black-fs-70">You agree that you are leveraging third party AI models and services separate from AIappOnsite, and that you have full control over and final responsibility for the prompts that will access the API as well as the end-user experience, and any representations made to them.</p>
     329
     330                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">4. Paid Accounts</p>
     331                        <p class="fs-regular-12 neutral-black-fs-70">Billing. If you purchase any services, you will provide complete and accurate billing information, including a valid payment method. For paid subscriptions, we will automatically charge your payment method on each agreed-upon periodic renewal until you cancel. You’re responsible for all applicable taxes, and we’ll charge tax when required. If your payment cannot be completed, we may downgrade your account or suspend your access to our services until payment is received.</p>
     332                        <p class="fs-regular-12 neutral-black-fs-70">Cancellation. You can cancel your paid subscription at any time. Previous payments are non-refundable,except where required by law. These Terms do not override any mandatory local laws regarding your cancellation rights.</p>
     333                        <p class="fs-regular-12 neutral-black-fs-70">Changes. We may change our prices from time to time. If we increase our subscription prices, we will give you at least 30 days’ notice and any price increase will take effect on your next renewal so that you can cancel if you do not agree to the price increase</p>
     334
     335                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">5. Termination and Suspension</p>
     336                        <p class="fs-regular-12 neutral-black-fs-70">Termination. You are free to stop using our Services at any time. We reserve the right to suspend or terminate your access to our Services or delete your account if we determine:
     337                        <ul class="pl-2">
     338                        <li class="fs-regular-12 neutral-black-fs-70">- You breached these Terms of Service.</li>
     339                        <li class="fs-regular-12 neutral-black-fs-70">- We must do so to comply with the law.</li>
     340                        <li class="fs-regular-12 neutral-black-fs-70">- Your use of our Services could cause risk or harm to AIappOnsite or anyone else.</li>
     341                        </ul>
     342                        </p>
     343                        <p class="fs-regular-12 neutral-black-fs-70">Appeals. If you believe we have suspended or terminated your account in error, you can file an appeal with us by contacting our Support team.</p>
     344
     345                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">6. Discontinuation of Services</p>
     346                        <p class="fs-regular-12 neutral-black-fs-70">We may decide to discontinue our Services, but if we do, we will give you advance notice and a refund for any prepaid, unused services.</p>
     347
     348                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">7. Disclaimer of Warranties</p>
     349                        <p class="fs-regular-12 neutral-black-fs-70">Our services are provided “as is.” except to the extent prohibited by law, we and our affiliates and licensors make no warranties (express, implied, statutory or otherwise) with respect to the services, and disclaim all warranties including, but not limited to, warranties of merchantability, fitness for a particular purpose,satisfactory quality, non-infringement, and quiet enjoyment, and any warranties arising out of any course of dealing or trade usage. We do not warrant that the services will be uninterrupted, accurate or error free, or that any content will be secure or not lost or altered.</p>
     350                        <p class="fs-regular-12 neutral-black-fs-70">You accept and agree that any use of the service is at your sole risk.</p>
     351
     352                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">8. Limitation of liability</p>
     353                        <p class="fs-regular-12 neutral-black-fs-70">Neither we nor any of our affiliates or licensors will be liable for any indirect,incidental, special, consequential, or exemplary damages, including damages for loss of profits, goodwill, use, or data or other losses, even if we have been advised of the possibility of such damages. Our aggregate liability under these terms will not exceed the greater of the amount you paid for the service that gave rise to the claim during the 12 months before the liability arose or one hundred dollars ($100).The limitations in this section apply only to the maximum extent permitted by applicable law.</p>
     354                        <p class="fs-regular-12 neutral-black-fs-70">Some countries and states do not allow the disclaimer of certain warranties or the limitation of certain damages, so some or all of the terms above may not apply to you, and you may have additional rights. In that case, these Terms only limit our responsibilities to the maximum extent permissible in your country of residence.</p>
     355                        <p class="fs-regular-12 neutral-black-fs-70">Aiapponsite’s affiliates, suppliers, licensors, and distributors are intended third party beneficiaries of this section.</p>
     356
     357                        <p class="fs-regular-14 neutral-black-fs-100 mb-1">9. Indemnity</p>
     358                        <p class="fs-regular-12 neutral-black-fs-70">If you are a business or organization, to the extent permitted by law, you will indemnify and hold harmless us,our affiliates, and our personnel, from and against any costs, losses, liabilities, and expenses (including attorneys’ fees) from third party claims arising out of or relating to your use of the Services and Content or any violation of these Terms.</p>
     359
     360                        <div class="terms-form-group form-group mb-0" id="terms-form-group">
     361                        <input type="checkbox" id="terms_conditions">
     362                        <label for="terms_conditions" class="fs-regular-14 pl-1 neutral-black-fs-90">Agree to Terms of Service</label>
     363                        </div>
     364                    </div>
     365                    <div class="divider-light mt-2 mb-3" id="divider"></div>
     366                    <div class="button-wrap" id="terms_conditions_button">
     367                        <button type="button" id="download_ai_app_terms_of_service" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2 bg-white" name="save-plugin-settings" onclick="ai_app_onsite_download_terms_Of_service()">Download</button>
     368                        <button type="button" id="accept_ai_app_terms_of_service" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2 bg-purple border-purple text-white" name="save-plugin-settings" data-bs-dismiss="modal">Accept</button>
     369                    </div>
     370                    </div>
     371                </div>
     372                </div>
    294373            </div>
    295             <div class="modal-body mb-1">
    296               <div id="terms-conditions-modal-pdf">
    297                 <p class="fs-regular-14 neutral-black-fs-100 mb-3 text-center">Terms of Services</p>
    298                 <p class="fs-regular-14 neutral-black-fs-90">Please read these terms carefully before using our service.</p>
    299                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">1. User Responsibility</p>
    300                 <p class="fs-regular-12 neutral-black-fs-70">If you reside in the European Economic Area, Switzerland, the UK, California or some other locations, you may be subject to special conditions and laws that govern the use of AI. These and other locales also have privacy and data protection rules and laws that may also dictate how you can leverage tools like AIappOnsite.</p>
    301                 <p class="fs-regular-12 neutral-black-fs-70">It is important that you abide by all such applicable laws. Furthermore, you may not use the Service in a way that infringes, misappropriates or violates anyone’s rights. You may also not use the Service in a way that endangers or harms, or intends to do the same, to end users of applications built with the Service.</p>
    302 
    303                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">2. Using AI Services</p>
    304                 <p class="fs-regular-12 neutral-black-fs-70">The Service requires that the site owner or their designee provide an API key which the Service will use tointegrate with an AI model provider. All AI functionality of the Service depends on one or more API keys, whichis provided by the various AI model vendors. An account must be opened with one of these vendors in order to get an API key.</p>
    305                 <p class="fs-regular-12 neutral-black-fs-70">These AI model providers are separate companies and unaffiliated with the Service, and have their own terms and conditions. Read them carefully. The API keys and their use are bound by the terms and conditions of the AI model provider and strictly controlled by the site owner or their designee (not AIappOnsite).</p>
    306 
    307                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">3. Limitation of Liability</p>
    308                 <p class="fs-regular-12 neutral-black-fs-70">Given that the site owner fully controls the use, purpose and output of any application built with it, AIappOnsite bears no responsibility or liability for how it is used, or to any consequences of its use by site owners or their site visitors.</p>
    309                 <p class="fs-regular-12 neutral-black-fs-70">You agree that you are leveraging third party AI models and services separate from AIappOnsite, and that you have full control over and final responsibility for the prompts that will access the API as well as the end-user experience, and any representations made to them.</p>
    310 
    311                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">4. Paid Accounts</p>
    312                 <p class="fs-regular-12 neutral-black-fs-70">Billing. If you purchase any services, you will provide complete and accurate billing information, including a valid payment method. For paid subscriptions, we will automatically charge your payment method on each agreed-upon periodic renewal until you cancel. You’re responsible for all applicable taxes, and we’ll charge tax when required. If your payment cannot be completed, we may downgrade your account or suspend your access to our services until payment is received.</p>
    313                 <p class="fs-regular-12 neutral-black-fs-70">Cancellation. You can cancel your paid subscription at any time. Previous payments are non-refundable,except where required by law. These Terms do not override any mandatory local laws regarding your cancellation rights.</p>
    314                 <p class="fs-regular-12 neutral-black-fs-70">Changes. We may change our prices from time to time. If we increase our subscription prices, we will give you at least 30 days’ notice and any price increase will take effect on your next renewal so that you can cancel if you do not agree to the price increase</p>
    315 
    316                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">5. Termination and Suspension</p>
    317                 <p class="fs-regular-12 neutral-black-fs-70">Termination. You are free to stop using our Services at any time. We reserve the right to suspend or terminate your access to our Services or delete your account if we determine:
    318                 <ul class="pl-2">
    319                   <li class="fs-regular-12 neutral-black-fs-70">- You breached these Terms of Service.</li>
    320                   <li class="fs-regular-12 neutral-black-fs-70">- We must do so to comply with the law.</li>
    321                   <li class="fs-regular-12 neutral-black-fs-70">- Your use of our Services could cause risk or harm to AIappOnsite or anyone else.</li>
    322                 </ul>
    323                 </p>
    324                 <p class="fs-regular-12 neutral-black-fs-70">Appeals. If you believe we have suspended or terminated your account in error, you can file an appeal with us by contacting our Support team.</p>
    325 
    326                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">6. Discontinuation of Services</p>
    327                 <p class="fs-regular-12 neutral-black-fs-70">We may decide to discontinue our Services, but if we do, we will give you advance notice and a refund for any prepaid, unused services.</p>
    328 
    329                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">7. Disclaimer of Warranties</p>
    330                 <p class="fs-regular-12 neutral-black-fs-70">Our services are provided “as is.” except to the extent prohibited by law, we and our affiliates and licensors make no warranties (express, implied, statutory or otherwise) with respect to the services, and disclaim all warranties including, but not limited to, warranties of merchantability, fitness for a particular purpose,satisfactory quality, non-infringement, and quiet enjoyment, and any warranties arising out of any course of dealing or trade usage. We do not warrant that the services will be uninterrupted, accurate or error free, or that any content will be secure or not lost or altered.</p>
    331                 <p class="fs-regular-12 neutral-black-fs-70">You accept and agree that any use of the service is at your sole risk.</p>
    332 
    333                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">8. Limitation of liability</p>
    334                 <p class="fs-regular-12 neutral-black-fs-70">Neither we nor any of our affiliates or licensors will be liable for any indirect,incidental, special, consequential, or exemplary damages, including damages for loss of profits, goodwill, use, or data or other losses, even if we have been advised of the possibility of such damages. Our aggregate liability under these terms will not exceed the greater of the amount you paid for the service that gave rise to the claim during the 12 months before the liability arose or one hundred dollars ($100).The limitations in this section apply only to the maximum extent permitted by applicable law.</p>
    335                 <p class="fs-regular-12 neutral-black-fs-70">Some countries and states do not allow the disclaimer of certain warranties or the limitation of certain damages, so some or all of the terms above may not apply to you, and you may have additional rights. In that case, these Terms only limit our responsibilities to the maximum extent permissible in your country of residence.</p>
    336                 <p class="fs-regular-12 neutral-black-fs-70">Aiapponsite’s affiliates, suppliers, licensors, and distributors are intended third party beneficiaries of this section.</p>
    337 
    338                 <p class="fs-regular-14 neutral-black-fs-100 mb-1">9. Indemnity</p>
    339                 <p class="fs-regular-12 neutral-black-fs-70">If you are a business or organization, to the extent permitted by law, you will indemnify and hold harmless us,our affiliates, and our personnel, from and against any costs, losses, liabilities, and expenses (including attorneys’ fees) from third party claims arising out of or relating to your use of the Services and Content or any violation of these Terms.</p>
    340 
    341                 <div class="terms-form-group form-group mb-0">
    342                   <input type="checkbox" id="terms_conditions">
    343                   <label for="terms_conditions" class="fs-regular-14 pl-1 neutral-black-fs-90">Agree to Terms of Service</label>
    344                 </div>
    345               </div>
    346               <div class="divider-light mt-2 mb-3"></div>
    347               <div class="button-wrap">
    348                 <button type="button" id="download_ai_app_terms_of_service" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2 bg-white" name="save-plugin-settings" onclick="ai_app_onsite_download_terms_Of_service()">Download</button>
    349                 <button type="button" id="accept_ai_app_terms_of_service" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2 bg-purple border-purple text-white" name="save-plugin-settings" data-bs-dismiss="modal">Accept</button>
    350               </div>
    351             </div>
    352           </div>
    353         </div>
    354       </div>
    355374
    356375<?php
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-app-preview.php

    r3281194 r3307540  
    5454                        <div class="row" id="system-settings-container">
    5555                            <p class="fs-6 font-semibold">Style Settings</p>
    56                             <form class="form-wrapper" method="POST" enctype="multipart/form-data">
     56                            <form class="form-wrapper" method="POST" id="app_preview_settings_form" enctype="multipart/form-data">
    5757                                <?php wp_nonce_field('ai_app_onsite_app_properties_nonce', 'ai_app_onsite_app_properties_nonce_field'); ?>
    5858                                <!-- Colors Selector -->
     
    6868                                                <div class="input-group-color">
    6969                                                    <label class="color-container title-color green-check">
    70                                                         <input type="radio" checked="checked" name="title-color" value="#5c5c5c">
     70                                                        <input type="radio" name="title-color" value="#5c5c5c" checked="checked">
    7171                                                        <span class="checkmark"></span>
    7272                                                    </label>
     
    9595                                                <div class="input-group-color">
    9696                                                    <label class="color-container font-color white-check">
    97                                                         <input type="radio" checked="checked" name="font-color" value="#8d949a">
     97                                                        <input type="radio"  name="font-color" value="#8d949a" checked="checked">
    9898                                                        <span class="checkmark"></span>
    9999                                                    </label>
     
    124124                                                <div class="input-group-color">
    125125                                                    <label class="color-container bg-color grey-check">
    126                                                         <input type="radio" checked="checked" name="bg-color" value="#f4f4f4">
     126                                                        <input type="radio" name="bg-color" value="#f4f4f4" checked="checked">
    127127                                                        <span class="checkmark"></span>
    128128                                                    </label>
     
    176176                                <div class="text-end mb-3" id="preview-action-container">
    177177                                    <button type="button" id="preview_ai_app_app_properties_data" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-white" name="preview-plugin-settings" onclick="ai_app_onsite_create_app_preview_form(true)">Preview</button>
    178                                     <button type="button" id="save_ai_app_app_properties_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" name="save-plugin-settings" onclick="ai_app_onsite_update_style_properties_only()">Save</button>
     178                                    <button type="button" id="save_ai_app_app_properties_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" id="ai_app_onsite_update_style_properties_only" onclick="ai_app_onsite_update_style_properties_only()">Save</button>
    179179                                </div>
    180180                            </form>
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-app-properties.php

    r3281194 r3307540  
    1010      <!--  Render your plugin setting tab page here -->
    1111      <div>
    12         <form class="form-wrapper" method="POST" enctype="multipart/form-data">
     12        <form class="form-wrapper" id="app-details-form" method="POST" enctype="multipart/form-data">
    1313          <?php wp_nonce_field('ai_app_onsite_app_properties_nonce', 'ai_app_onsite_app_properties_nonce_field'); ?>
    1414          <div class="form-group mb-20">
     
    2020                  <div class="assets-folder-icon"></div>
    2121                </span>
    22                 <input type="text" name="app-name" id="app-name" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60 radius-6 px-10 py-8">
     22                <input type="text" name="app-name" id="app-name" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60 radius-6 px-10 py-8 app-name" disabled>
    2323                <p id="app-name-error" class="fields-error"></p>
    2424              </div>
     
    3535          </div>
    3636          <div class="form-group">
    37             <label class="fs-medium-14 mb-1">App Logo/Image</label>
     37            <label class="fs-medium-14 mb-1">App Image</label>
    3838            <div class="col-12 d-flex flex-column flex-md-row">
    3939              <!-- First div with the file input and preview -->
     
    5959                        <div class="assets-cross-icon"></div> <!-- You can replace this with an actual cross icon -->
    6060                      </span>
    61                       <span>Remove Logo</span>
     61                      <span>Remove Image</span>
    6262                    </button>
    6363
     
    7777                <div class="assets-pen-icon"></div>
    7878              </span>
    79               <textarea class="border text-write w-100 fs-regular-14 neutral-black-fs-100 radius-6" id="app-description" rows="4" cols="50" placeholder=""></textarea>
     79              <textarea class="border text-write w-100 fs-regular-14 neutral-black-fs-100 radius-6" id="app-description" name="app-description" rows="4" cols="50" placeholder=""></textarea>
    8080              <p class="neutral-black-fs-60 fs-regular-12 mb-0" id="wordCount">275 characters left</p>
    8181              <p id="app-description-error" class="fields-error"></p>
     
    8888                <div class="assets-pen-icon"></div>
    8989              </span>
    90               <textarea id="app-disclaimer" class="border text-write w-100 fs-regular-14 neutral-black-fs-100 radius-6 " placeholder=""></textarea>
     90              <textarea id="app-disclaimer" class="border text-write w-100 fs-regular-14 neutral-black-fs-100 radius-6 "  name="app-disclaimer" placeholder=""></textarea>
    9191              <p class="neutral-black-fs-60 fs-regular-12 mb-0">Note: This app uses OpenAI to generate suggestions and feedback. Never include any personal identifying data when using AI tools.</p>
    9292              <p id="app-disclaimer-error" class="fields-error"></p>
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-db-handler.php

    r3281194 r3307540  
    99        protected $app_properties;
    1010        protected $model_settings;
     11        protected $license_settings;
    1112        protected $field_selector_settings;
    1213        protected $prompt_editor;
     
    1617        {
    1718
    18             $this->wpdb = $wpdb;
    19             $this->plugin_settings = $wpdb->prefix . 'ai_app_onsite_plugin_settings';
    20             $this->app_properties = $wpdb->prefix . 'ai_app_onsite_app_properties';
    21             $this->model_settings = $wpdb->prefix . 'ai_app_onsite_model_settings';
     19            $this->wpdb                    = $wpdb;
     20            $this->plugin_settings         = $wpdb->prefix . 'ai_app_onsite_plugin_settings';
     21            $this->app_properties          = $wpdb->prefix . 'ai_app_onsite_app_properties';
     22            $this->model_settings          = $wpdb->prefix . 'ai_app_onsite_model_settings';
     23            $this->license_settings        = $wpdb->prefix . 'ai_app_onsite_license_settings';
    2224            $this->field_selector_settings = $wpdb->prefix . 'ai_app_onsite_field_selector_settings';
    23             $this->prompt_editor = $wpdb->prefix . 'ai_app_onsite_prompt_editor';
    24             $this->user_statistics = $wpdb->prefix . 'ai_app_onsite_user_statistics';
     25            $this->prompt_editor           = $wpdb->prefix . 'ai_app_onsite_prompt_editor';
     26            $this->user_statistics         = $wpdb->prefix . 'ai_app_onsite_user_statistics';
    2527        }
    2628
     
    3335            $app_properties = $this->app_properties;
    3436            $model_settings = $this->model_settings;
     37            $license_settings = $this->license_settings;
    3538            $field_selector_settings = $this->field_selector_settings;
    3639            $prompt_editor = $this->prompt_editor;
     
    4144            $app_properties_table =  $app_properties;
    4245            $model_settings_table = $model_settings;
     46            $license_settings_table = $license_settings;
    4347            $field_selector_settings_table = $field_selector_settings;
    4448            $prompt_editor_table =  $prompt_editor;
     
    9599               )";
    96100
     101            $sql_license_settings = "CREATE TABLE IF NOT EXISTS $license_settings_table (
     102                id INT(11) NOT NULL AUTO_INCREMENT,
     103                app_id INT(30) NOT NULL,
     104                license_key VARCHAR(255),
     105                key_status VARCHAR(11),
     106                subscription_expiry_date VARCHAR(255),
     107                key_disable_status ENUM('true', 'false') DEFAULT 'false',
     108                created_at DATETIME NOT NULL,
     109                updated_at DATETIME NOT NULL,
     110                PRIMARY KEY (id)
     111            )";
     112
    97113            $sql_field_selector_settings = "CREATE TABLE IF NOT EXISTS $field_selector_settings_table (
    98114                   id INT(30) NOT NULL AUTO_INCREMENT,
     
    148164                $sql_app_properties,
    149165                $sql_model_settings,
     166                $sql_license_settings,
    150167                $sql_field_selector_settings,
    151168                $sql_prompt_editor,
     
    169186            $app_properties_table = $this->app_properties;
    170187            $model_settings_table =  $this->model_settings;
     188            $license_settings_table =  $this->license_settings;
    171189            $field_selector_settings_table =  $this->field_selector_settings;
    172190            $prompt_editor_table = $this->prompt_editor;
     
    177195            $this->wpdb->query("DROP TABLE IF EXISTS $app_properties_table");
    178196            $this->wpdb->query("DROP TABLE IF EXISTS $model_settings_table");
     197            $this->wpdb->query("DROP TABLE IF EXISTS $license_settings_table");
    179198            $this->wpdb->query("DROP TABLE IF EXISTS $field_selector_settings_table");
    180199            $this->wpdb->query("DROP TABLE IF EXISTS $prompt_editor_table");
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-field-selector.php

    r3250856 r3307540  
    88            ob_start(); ?>
    99            <div class="field-select-content">
    10                 <div id="myForm" class="container">
     10                <form id="myForm" class="container">
    1111                    <?php wp_nonce_field('ai_app_onsite_field_selector_nonce', 'ai_app_onsite_field_selector_nonce_field'); ?>
    1212                    <div class="row alignment-items-center mb-4">
     
    1414                            <div class="select-percentage">
    1515                                <label class="fs-medium-14 mb-0">App Width</label>
    16                                 <select id="app_width_figure" class="number-select">
     16                                <select id="app_width_figure" name="app_width_figure" class="number-select">
    1717                                    <option value="30">30%</option>
    1818                                    <option value="50">50%</option>
     
    4343                        <button type="button" id="save_ai_app_ai_app_fieldselector_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" name="save-plugin-settings" onclick="ai_app_onsite_save_ai_app_fieldselector_data()">Save</button>
    4444                    </div>
    45                 </div>
     45                </form>
    4646            </div>
    4747            <div id="field-selector-msg-container" class="message-container"></div>
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-model-settings.php

    r3272415 r3307540  
    1111            <div class="modal-setting-content">
    1212                <div id="modal-setting-msg-container"></div>
    13                 <form class="form-wrapper" id="ai_app_onsite_reset_modal-setting">
    14                     <?php wp_nonce_field('ai_app_onsite_model_settings_nonce', 'ai_app_onsite_model_settings_nonce_field'); ?>
     13                <form class="form-wrapper" id="ai_app_onsite_reset_modal-setting"> <?php wp_nonce_field('ai_app_onsite_model_settings_nonce', 'ai_app_onsite_model_settings_nonce_field'); ?>
    1514                    <div class="row">
    1615                        <div class="col-lg-12">
     
    2019                                    <div class="assets-engine-icon"></div>
    2120                                </span>
    22                                 <select id="model" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60 radius-6 px-10 py-8">
     21                                <select id="model" name="model" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60 radius-6 px-10 py-8">
    2322                                    <option value="">Please Select Model</option>
    2423                                    <option value="gpt-4o">Open AI - gpt-4o</option>
     
    3433                        </div>
    3534                    </div>
    36             <div id="model-dependent-fields" class="row">
    37 
    38                         <div class="col-lg-6 mb-20">
    39                             <div class="form-group">
    40                                 <label class="fs-medium-14 font-semibold">Max Tokens</label>
    41                                 <div class="select-box-icon">
    42                                     <span class="field-icon">
    43                                         <div class="assets-token-icon"></div>
    44                                     </span>
    45                                     <input type="text" name="" placeholder="3000" id="max-tokens" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="3000">
    46                                     <span class="small-text text-muted">Maximum number of tokens (words & characters) allowed in response.</span>
    47                                 </div>
    48                             </div>
     35                    <div id="model-dependent-fields" class="row">
     36                    <div class="col-lg-6 mb-20">
     37                        <div class="form-group">
     38                        <label class="fs-medium-14 font-semibold">Max Tokens</label>
     39                        <div class="select-box-icon">
     40                            <span class="field-icon">
     41                            <div class="assets-token-icon"></div>
     42                            </span>
     43                            <input type="text" name="max-tokens" placeholder="3000" id="max-tokens" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="3000">
     44                            <span class="small-text text-muted">Maximum number of tokens (words & characters) allowed in response.</span>
    4945                        </div>
    50                         <div class="col-lg-6 mb-20">
    51                             <div class="form-group">
    52                                 <label class="fs-medium-14 font-semibold">Temperature</label>
    53                                 <div class="select-box-icon">
    54                                     <span class="field-icon">
    55                                         <div class="assets-temprature-icon"></div>
    56                                     </span>
    57                                     <input type="text" name="temperature" placeholder="1" id="temperature" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="0.7">
    58                                     <span class="small-text text-muted">Controls creativity — higher value = more random responses.</span>
    59                                 </div>
    60                             </div>
    6146                        </div>
    62                         <div class="col-lg-6 mb-20">
    63                             <div class="form-group">
    64                                 <label class="fs-medium-14 font-semibold">Top P</label>
    65                                 <div class="select-box-icon">
    66                                     <span class="field-icon">
    67                                         <div class="assets-temprature-icon"></div>
    68                                     </span>
    69                                     <input type="text" name="top-p" placeholder="1" id="top-p" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="1">
    70                                     <span class="small-text text-muted">Controls diversity — lower value = more focused responses.</span>
    71                                 </div>
    72                             </div>
     47                    </div>
     48                    <div class="col-lg-6 mb-20">
     49                        <div class="form-group">
     50                        <label class="fs-medium-14 font-semibold">Temperature</label>
     51                        <div class="select-box-icon">
     52                            <span class="field-icon">
     53                            <div class="assets-temprature-icon"></div>
     54                            </span>
     55                            <input type="text" name="temperature" placeholder="1" id="temperature" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="0.7">
     56                            <span class="small-text text-muted">Controls creativity — higher value = more random responses.</span>
    7357                        </div>
    74                         <div class="col-lg-6 mb-20">
    75                             <div class="form-group mb-0">
    76                                 <label class="fs-medium-14 font-semibold">F. Penalty</label>
    77                                 <div class="select-box-icon">
    78                                     <span class="field-icon">
    79                                         <div class="assets-penalty-icon"></div>
    80                                     </span>
    81                                     <input type="text" name="f-penalty" placeholder="0" id="f-penalty" class="border border-1 w-100  fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="0">
    82                                     <span class="small-text text-muted">Reduces repetition of existing words in the response.</span>
    83                                 </div>
    84                             </div>
    8558                        </div>
    86                         <div class="col-lg-6 mb-20">
    87                             <div class="form-group mb-0">
    88                                 <label class="fs-medium-14 font-semibold">P. Penalty</label>
    89                                 <div class="select-box-icon">
    90                                     <span class="field-icon">
    91                                         <div class="assets-penalty-icon"></div>
    92                                     </span>
    93                                     <input type="text" name="p-penalty" placeholder="0" id="p-penalty" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="0">
    94                                     <span class="small-text text-muted">Reduces repetition of new words in the response.</span>
    95                                 </div>
    96                             </div>
     59                    </div>
     60                    <div class="col-lg-6 mb-20">
     61                        <div class="form-group">
     62                        <label class="fs-medium-14 font-semibold">Top P</label>
     63                        <div class="select-box-icon">
     64                            <span class="field-icon">
     65                            <div class="assets-temprature-icon"></div>
     66                            </span>
     67                            <input type="text" name="top-p" placeholder="1" id="top-p" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="1">
     68                            <span class="small-text text-muted">Controls diversity — lower value = more focused responses.</span>
    9769                        </div>
    98             </div>
    99                
     70                        </div>
     71                    </div>
     72                    <div class="col-lg-6 mb-20">
     73                        <div class="form-group mb-0">
     74                        <label class="fs-medium-14 font-semibold">F. Penalty</label>
     75                        <div class="select-box-icon">
     76                            <span class="field-icon">
     77                            <div class="assets-penalty-icon"></div>
     78                            </span>
     79                            <input type="text" name="f-penalty" placeholder="0" id="f-penalty" class="border border-1 w-100  fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="0">
     80                            <span class="small-text text-muted">Reduces repetition of existing words in the response.</span>
     81                        </div>
     82                        </div>
     83                    </div>
     84                    <div class="col-lg-6 mb-20">
     85                        <div class="form-group mb-0">
     86                        <label class="fs-medium-14 font-semibold">P. Penalty</label>
     87                        <div class="select-box-icon">
     88                            <span class="field-icon">
     89                            <div class="assets-penalty-icon"></div>
     90                            </span>
     91                            <input type="text" name="p-penalty" placeholder="0" id="p-penalty" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" value="0">
     92                            <span class="small-text text-muted">Reduces repetition of new words in the response.</span>
     93                        </div>
     94                        </div>
     95                    </div>
     96                    </div>
    10097                    <div class="divider-light"></div>
    10198                    <div class="row justify-content-center">
    102                         <div class="col-lg-6" id="api-key-management">
    103                             <div class="api-key-icon select-box-icon d-flex justify-content-around align-items-center" onclick="ai_app_onsite_get_openapi_data()">
    104                                 <span class="#">
    105                                     <div class="assets-api-key-icon"></div>
    106                                 </span>
    107                                 <p class="mb-0 fs-6 cursor-pointer" data-bs-toggle="modal" data-bs-target="#apiinfoModal">
    108                                     API Key Management
    109                                 </p>
    110 
    111                                 <span class="mb-0">
    112                                     <p id="key-status" class="mb-0 font-semibold fs-6"></p>
    113                                 </span>
    114                             </div>
    115 
    116                             <p id="manage-key-error" class="mt-2 ml-3 fields-error"></p>
     99                    <div class="col-lg-6" id="api-key-management">
     100                        <div class="api-key-icon select-box-icon d-flex justify-content-around align-items-center" onclick="ai_app_onsite_get_openapi_data()">
     101                        <span class="#">
     102                            <div class="assets-api-key-icon"></div>
     103                        </span>
     104                        <p class="mb-0 fs-6 cursor-pointer" data-bs-toggle="modal" data-bs-target="#apiinfoModal"> API Key Management </p>
     105                        <span class="mb-0">
     106                            <p id="key-status" class="mb-0 font-semibold fs-6"></p>
     107                        </span>
    117108                        </div>
    118                         <div class="col-lg-6">
    119                             <div class="text-end">
    120                                 <button type="button" id="cancel_ai_app_plugin_settings_data" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-white" name="save-plugin-settings" onclick="reset()">Reset to Default</button>
    121                                 <button type="button" id="save_ai_app_plugin_settings_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" name="save-plugin-settings" onclick="ai_app_onsite_save_model_settings()">Save</button>
    122                             </div>
     109                        <p id="manage-key-error" class="mt-2 ml-3 fields-error"></p>
     110                    </div>
     111                    <div class="col-lg-6">
     112                        <div class="text-end">
     113                        <button type="button" id="cancel_ai_app_plugin_settings_data" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-white" name="save-plugin-settings" onclick="reset()">Reset to Default</button>
     114                        <button type="button" id="save_ai_app_modal_settings_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" name="save-plugin-settings" onclick="ai_app_onsite_save_model_settings()">Save</button>
    123115                        </div>
    124116                    </div>
     117                    </div>
    125118                </form>
    126 
    127119                <!-- Modal -->
    128120                <div class="modal fade bd-example-modal-sm plugin-setting" id="apiinfoModal" tabindex="-1" role="dialog" aria-labelledby="apiinfoModalLabel" aria-hidden="true">
    129121                    <div class="modal-dialog modal-sm modal-400 modal-dialog-centered" role="document">
    130                         <div class="modal-content">
    131                             <div id="modal-message-container" class="message-container"></div>
    132                             <div class="modal-header">
    133                                 <h5 class="fs-bold-16 mb-0 logo-bg-fixed-sm" id="terms_conditions_modal_label">
    134                                 </h5>
    135                                 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
     122                    <div class="modal-content">
     123                        <div id="modal-message-container" class="message-container"></div>
     124                        <div class="modal-header">
     125                        <h5 class="fs-bold-16 mb-0 logo-bg-fixed-sm" id="terms_conditions_modal_label"></h5>
     126                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
     127                        </div>
     128                        <div class="modal-body">
     129                        <p class="fs-regular-14 neutral-black-fs-100">Please enter your OpenAI API key below. You can get an API key from your <a href="https://openai.com/api/" class="neutral-black-fs-100 underline open-new-tab"> OpenAI account dashboard.</a>
     130                        </p>
     131                        <div class="form-group mb-0">
     132                            <label class="fs-medium-14 fw-semibold">API Key</label>
     133                            <div class="select-box-icon right-icon">
     134                            <span class="field-icon copy-api-key">
     135                                <div class="assets-copy-icon"></div>
     136                            </span>
     137                            <input type="text" name="openai-key" placeholder="Enter text" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" id="openai-key">
     138                            <input type="hidden" name="openai-key-hidden" id="openai-key-hidden">
    136139                            </div>
    137                             <div class="modal-body">
    138                                 <p class="fs-regular-14 neutral-black-fs-100">Please enter your OpenAI API key below. You can get an API key from your <a href="" class="neutral-black-fs-100 underline"> OpenAI account dashboard.</a></p>
    139                                 <div class="form-group mb-0">
    140                                     <label class="fs-medium-14 fw-semibold">API Key</label>
    141                                     <div class="select-box-icon right-icon">
    142                                         <span class="field-icon copy-api-key">
    143                                             <div class="assets-copy-icon"></div>
    144                                         </span>
    145                                         <input type="text" name="openai-key" placeholder="Enter text" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" id="openai-key">
    146                                         <input type="hidden" name="openai-key-hidden" id="openai-key-hidden">
    147                                     </div>
    148                                     <div class="d-flex justify-content-start align-items-center mt-2 key-toggle">
    149                                         <span id="maskToggle" class="text-muted me-3 mask__toggle">Show Key</span>
    150                                     </div>
    151                                     <p id="openai-key-error" class="fields-error"></p>
    152                                 </div>
    153                                 <p class="fs-regular-12 neutral-black-fs-60 mb-1 mt-2">Note: Your API key will be stored locally and securely in your browser and never shared.</p>
    154                                 <div class="flex-row alignment-items-center mt-3 mb-3">
    155                                     <div class="col-50">
    156                                     <button class="btn remove-btn align-items-center" id="key-remove-btn" onclick="ai_app_onsite_clear_openapi_Key_field()">
    157                                             <span class="btn-icon me-1">
    158                                                 <div class="assets-delete-icon"></div>
    159                                             </span> Remove API Key
    160                                         </button>
    161                                     </div>
    162                                     <div class="col-50">
    163                                         <div class="enable-switch-box d-flex justify-content-between align-items-baseline">
    164                                             <span class="fs-medium-14 text-switch me-2" id="text-switch">Disabled</span>
    165                                             <label class="switch">
    166                                                 <input type="checkbox" id="api-disable-btn" onchange="ai_app_onsite_disable_openapi_key()">
    167                                                 <span class="slider round"></span>
    168                                             </label>
    169                                         </div>
    170                                     </div>
    171                                 </div>
    172                                 <div class="divider-light mt-2 mb-3"></div>
    173                                 <div class="button-wrap">
    174                                     <button type="button" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border  border-2 bg-white" name="save-plugin-settings" data-bs-dismiss="modal">Cancel</button>
    175                                     <button type="button" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2  bg-purple text-white" name="save-openapi-key" id="save-openapi-key" onclick="save_openapi_key()">Save</button>
    176                                 </div>
     140                            <div class="d-flex justify-content-start align-items-center mt-2 key-toggle">
     141                            <span id="maskToggle" class="text-muted me-3 mask__toggle">Show Key</span>
     142                            </div>
     143                            <p id="openai-key-error" class="fields-error"></p>
     144                        </div>
     145                        <p class="fs-regular-12 neutral-black-fs-60 mb-1 mt-2">Note: Your API key will be stored locally and securely in your browser and never shared.</p>
     146                        <div class="flex-row alignment-items-center mt-3 mb-3">
     147                            <div class="col-50">
     148                            <button class="btn remove-btn align-items-center" id="key-remove-btn" onclick="ai_app_onsite_clear_openapi_Key_field()">
     149                                <span class="btn-icon me-1">
     150                                <div class="assets-delete-icon"></div>
     151                                </span> Remove API Key </button>
     152                            </div>
     153                            <div class="col-50">
     154                            <div class="enable-switch-box d-flex justify-content-between align-items-baseline">
     155                                <span class="fs-medium-14 text-switch me-2" id="text-switch">Disabled</span>
     156                                <label class="switch">
     157                                <input type="checkbox" id="api-disable-btn" onchange="ai_app_onsite_disable_openapi_key()">
     158                                <span class="slider round"></span>
     159                                </label>
     160                            </div>
    177161                            </div>
    178162                        </div>
     163                        <div class="divider-light mt-2 mb-3"></div>
     164                        <div class="button-wrap">
     165                            <button type="button" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border  border-2 bg-white" name="save-plugin-settings" data-bs-dismiss="modal">Cancel</button>
     166                            <button type="button" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2  bg-purple text-white" name="save-openapi-key" id="save-openapi-key" onclick="save_openapi_key()">Save</button>
     167                        </div>
     168                        </div>
     169                    </div>
    179170                    </div>
    180171                </div>
     172                <!-- Modal -->
    181173            </div>
    182174            <div id="model-settings-msg-container" class="message-container"></div>
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-plugin-settings.php

    r3281201 r3307540  
    11<?php
    2 if (! class_exists('AI_App_Onsite_Plugin_Settings')) {
    3     class AI_App_Onsite_Plugin_Settings
    4     {
    5         public static function render_plugin_settings()
    6         {
    7             $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/ai-app-onsite/ai-app-onsite.php');
    8             $version = $plugin_data['Version'];
    9             ob_start(); ?>
    10             <div class="form-wrapper">
    11                 <form enctype="multipart/form-data">
    12                     <?php wp_nonce_field('ai_app_onsite_plugin_settings_nonce', 'ai_app_onsite_plugin_settings_nonce_field'); ?>
    13                     <div class="mb-20">
    14                         <div class="d-flex justify-content-between mb-0 align-items-center">
    15                             <p class="font-bold fs-5 mb-0">Get Started</p>
    16                             <p class="fs-regular-14 neutral-black-fs-60 mb-0">Version: <?php echo $version; ?></p>
     2if (!class_exists("AI_App_Onsite_Plugin_Settings")) {
     3  class AI_App_Onsite_Plugin_Settings
     4  {
     5    public static function render_plugin_settings()
     6    {
     7      $plugin_data = get_plugin_data(plugin_dir_path(__DIR__) . "/ai-app-onsite.php");
     8      $version = $plugin_data["Version"];
     9      ob_start();
     10?>
     11  <div class="app-plugin-settings">
     12    <div class="form-wrapper">
     13        <div class="col-12 col-md-12">
     14            <div class="row d-flex align-items-center mb-2">
     15                <div class="col-md-6">
     16                    <p class="font-bold fs-5 mb-0">Access Key</p>
     17                </div>
     18                <div class="col-md-6">
     19                    <div class="api-key-icon d-flex justify-content-end align-items-center">
     20                        <span class="px-3" >
     21                            <div class="assets-api-key-icon"> </div>
     22                        </span>
     23                        <p class="fs-7 cursor-pointer mb-0" data-bs-toggle="modal" data-bs-target="#license_key_info_modal">   
     24                             Key Management
     25                        </p>
     26                    </div>
     27                </div>
     28            </div> 
     29            <div class="row d-flex align-items-center">
     30                <div class="col-md-12">
     31                <p class="fs-7 mb-0">Unlock powerful extra features with an AIappOnsite premium plan. <a href="https://aiapponsite.com/pricing" class="open-new-tab">Learn more</a> and get your access key now</p>
     32                </div>
     33            </div> 
     34            <div class="row d-flex justify-content-center mt-3">
     35                <div class="col-12 col-md-4 align-items-center mb-3" id="license_status">
     36                    <div class="row d-flex">
     37                        <label for="licence_key" class="d-block fs-regular-10 mb-only-4">Verification Status</label>
     38                        <p class="font-bold mb-1" id="license_key_status"></p>
     39                    </div>
     40                </div>
     41                <div class="col-12 col-md-4 align-items-center mb-3" id="expiry_status">
     42                    <div class="row d-flex">
     43                        <label for="licence_key" class="d-block fs-regular-10 mb-only-4">Exipry Date</label>
     44                        <p class="font-bold mb-1" id="license_key_expiry_date"></p>
     45                    </div>
     46                </div>
     47                <div class="col-12 col-md-4 align-items-center mb-3" id="key_status">
     48                    <div class="row d-flex">
     49                        <label for="licence_key" class="d-block fs-regular-10 mb-only-4">Key Status</label>
     50                        <p class="font-bold mb-1" id="license_key_disabled_status"></p>
     51                    </div>
     52                </div>
     53            </div>
     54            <div class="divider-light mt-2 mb-2"></div>
     55        </div>
     56        <form enctype="multipart/form-data" id="app_settings_form">
     57            <?php wp_nonce_field("ai_app_onsite_plugin_settings_nonce","ai_app_onsite_plugin_settings_nonce_field"); ?> <div class="mb-20">
     58                <div class="d-flex justify-content-between mb-0 align-items-center">
     59                    <p class="font-bold fs-5 mb-0">Get Started</p>
     60                    <p class="fs-regular-14 neutral-black-fs-60 mb-0">Version: <?php echo $version; ?> </p>
     61                </div>
     62                <div class="terms-form-group form-group mb-0">
     63                    <input type="checkbox" id="terms_of_service-old" name="terms_of_service-old" onchange="uploadTermCondition(this)">
     64                    <label for="terms_of_service-old" data-toggle="modal" data-target=".bd-example-modal-sm" class="custom-checkbox fs-medium-14 ">
     65                        <span class="ml-1">Agree to Terms of Service</span>
     66                    </label>
     67                </div>
     68                <p class="fs-regular-14 ml-25 mb-1 text-muted"> You must agree to the terms of service to use this plugin. Please read the terms carefully before proceeding. </p>
     69                <p id="terms_of_service-error" class="ml-25 fields-error"></p>
     70            </div>
     71            <div class="mb-3 app_selector_input">
     72                <label for="app_selector_input" class="d-block fs-medium-14 mb-only-4 app_selector_input_label" >Create Your First App <span class="text-danger">*</span>
     73                </label>
     74                <div class="select-box-icon">
     75                    <span class="field-icon">
     76                        <div class="assets-folder-icon"></div>
     77                    </span>
     78                    <input type="text" id="app_selector" name="app_selector" placeholder="Please Enter Your App Name" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60 radius-6 px-10 py-8">
     79                </div>
     80            </div>
     81            <div class="mb-3">
     82                <label for="email-notification" class="d-block fs-medium-14 mb-only-4">App Email (for form data passing) <span class="fs-regular-14"></span>
     83                </label>
     84                <div class="select-box-icon">
     85                    <span class="field-icon">
     86                        <div class="assets-email-icon"></div>
     87                    </span>
     88                    <input type="email" name="app_email" id="app_email" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8">
     89                </div>
     90            </div>
     91            <div class="mb-3">
     92                <label class="d-block fs-medium-14 mb-only-4">Banned/blocked words (comma Separated)</label>
     93                <div class="multi-select-container select-box-icon mb-20 fs-regular-14 neutral-black-fs-100 border border-1  radius-6 px-10 py-8">
     94                    <span class="field-icon">
     95                        <div class="assets-banned-icon"></div>
     96                    </span>
     97                    <div class="multi-select">
     98                        <select multiple data-multi-select-plugin id="bannedWords" name="bannedWords"></select>
     99                    </div>
     100                </div>
     101            </div>
     102            <div class="form-group">
     103                <label class="fs-medium-14">Upload Banned/Block CSV</label>
     104                <div class="file-drop-area border ">
     105                    <span class="fake-btn">
     106                        <div class="assets-upload-file-icon"></div>
     107                    </span>
     108                    <div>
     109                        <p class="file-msg-top mb-0 neutral-black-fs-110">
     110                            <span class="text-purple">Click to upload</span> or drag and drop
     111                        </p>
     112                        <p class="neutral-black-fs-50 mb-0 fs-regular-12">CSV</p>
     113                    </div>
     114                    <input class="file-input" type="file" id="csv-upload" name="csv-file" accept=".csv" onchange="validateCSVFile(this)">
     115                </div>
     116                <p id="fileError" class="mt-1 fields-error"></p>
     117            </div>
     118            <div class="divider-light"></div>
     119            <div class="text-end">
     120                <button type="button" id="cancel_ai_app_plugin_settings_data" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-white" name="save-plugin-settings">Cancel</button>
     121                <button type="button" id="save_ai_app_plugin_settings_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" name="save-plugin-settings" onclick="ai_app_onsite_save_plugin_settings()">Save</button>
     122            </div>
     123        </form>
     124    </div>
     125    <!-- Modal -->
     126    <div class="modal fade bd-example-modal-sm plugin-setting" id="license_key_info_modal" tabindex="-1" role="dialog" aria-labelledby="license_key_info_modalLabel" aria-hidden="true">
     127        <div class="modal-dialog modal-sm modal-400 modal-dialog-centered" role="document">
     128            <div class="modal-content">
     129                <div id="modal-message-container" class="message-container"></div>
     130                <div class="modal-header">
     131                    <h5 class="fs-bold-16 mb-0" id="terms_conditions_modal_label">AIappOnsite Access Key</h5>
     132                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
     133                </div>
     134                <div class="modal-body">
     135                    <form method="POST" id="license-validation-form" enctype="multipart/form-data">
     136                    <?php wp_nonce_field('ai_app_onsite_app_verify_license_key_nonce', 'ai_app_onsite_app_verify_license_key_nonce_field'); ?>
     137                        <p class="fs-regular-14 neutral-black-fs-100">Enter your access key to enable Aiapponsite premium features. For more details, visit our <a href="https://aiapponsite.com/pricing" class="open-new-tab">pricing page</a>. </p>
     138                        <div class="form-group mb-0">
     139                            <label class="fs-medium-14 fw-semibold">Access Key *</label>
     140                            <div class="select-box-icon right-icon">
     141                                <input type="text" id="licence_key" name="licence_key" class="border border-1 w-100 fs-regular-14 neutral-black-fs-60 radius-6 px-10 py-8 mt-2" required>
     142                                <input type="hidden" id="licence_key_hidden" name="licence_key_hidden">
     143                            </div>
     144                        </div>
     145                        <div class="row align-items-center my-2">
     146                            <div class="col-md-12" id="key_note">
     147                                <p class="fs-regular-12 text-opacity-50 text-dark fst-italic">Enter your Premium Add-On access key.</p>
     148                            </div>
     149                        </div>
     150                        <div class="divider-light mt-2 mb-3"></div>
     151                        <div class="row d-flex align-items-center mt-3 mb-3">
     152                            <div class="col-md-12">
     153                                <p class="text-muted text-center">You can find and manage your access key on <a href="https://aiapponsite.com/my-account/" class="open-new-tab">My Account</a>. </p>
     154                            </div>
     155                        </div>
     156                        <div class="flex-row alignment-items-center mt-3 mb-3">
     157                            <div class="col-50">
     158                            <button type="button" class="btn remove-btn align-items-center" id="remove_ai_app_plugin_license_key" onclick="ai_app_onsite_app_remove_license_key()">
     159                                <span class="btn-icon me-1">
     160                                <div class="assets-delete-icon"></div>
     161                                </span> Remove Key</button>
     162                            </div>
     163                            <div class="col-50">
     164                            <div class="enable-switch-box d-flex justify-content-between align-items-baseline">
     165                                <span class="fs-medium-14 text-switch me-2" id="license-text-switch">Disabled</span>
     166                                <label class="switch">
     167                                    <input type="checkbox" id="key-disable-btn" onchange="ai_app_onsite_disable_license_key()">
     168                                    <span class="slider round"></span>
     169                                </label>
     170                            </div>
     171                            </div>
    17172                        </div>
    18                         <div class="terms-form-group form-group mb-0">
    19                             <input type="checkbox" id="terms_of_service-old" onchange="uploadTermCondition(this)">
    20                             <label for="terms_of_service-old" data-toggle="modal" data-target=".bd-example-modal-sm" class="custom-checkbox fs-medium-14 "><span class="ml-1">Agree to Terms of Service</span></label>
    21                         </div>
    22                         <p class="fs-regular-14 ml-25 mb-1 text-muted">
    23                             You must agree to the terms of service to use this plugin. Please read the terms carefully before proceeding.
    24                         </p>
    25                         <p id="terms_of_service-error" class="ml-25 fields-error"></p>
    26                     </div>
    27 
    28                     <div class="mb-3">
    29                         <label for="app-selector" class="d-block fs-medium-14 mb-only-4">App/Project Selector
    30                             <span class="fs-regular-14">(Multiple Apps With Premium)</span></label>
    31                         <div class="select-box-icon">
    32               <span class="field-icon">
    33                 <div class="assets-folder-icon"></div>
    34               </span>
    35                             <input type="text" id="app_selector" name="app_selector" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8" disabled>
    36                         </div>
    37                     </div>
    38 
    39 
    40           <div class="mb-3">
    41             <label for="email-notification" class="d-block fs-medium-14 mb-only-4">App Email (for form data passing)
    42               <span class="fs-regular-14"></span></label>
    43             <div class="select-box-icon">
    44               <span class="field-icon">
    45                 <div class="assets-email-icon"></div>
    46               </span>
    47               <input type="email" name="app_email" id="app_email" class="border border-1 w-100 mb-1 fs-regular-14 neutral-black-fs-60  radius-6 px-10 py-8">
    48             </div>
    49           </div>
    50 
    51 
    52           <div class="mb-3">
    53             <label class="d-block fs-medium-14 mb-only-4">Banned/blocked words (comma Separated)</label>
    54             <div class="multi-select-container select-box-icon mb-20 fs-regular-14 neutral-black-fs-100 border border-1  radius-6 px-10 py-8">
    55               <span class="field-icon">
    56                 <div class="assets-banned-icon"></div>
    57               </span>
    58               <div class="multi-select">
    59                 <select multiple data-multi-select-plugin id="bannedWords">
    60 
    61                 </select>
    62               </div>
    63             </div>
    64 
    65           </div>
    66           <div class="form-group">
    67             <label class="fs-medium-14">Upload Banned/Block CSV</label>
    68             <div class="file-drop-area border ">
    69               <span class="fake-btn">
    70                 <div class="assets-upload-file-icon"></div>
    71               </span>
    72               <div>
    73                 <p class="file-msg-top mb-0 neutral-black-fs-110"><span class="text-purple">Click to upload</span> or drag and drop</p>
    74                 <p class="neutral-black-fs-50 mb-0 fs-regular-12">CSV</p>
    75               </div>
    76               <input class="file-input" type="file" id="csv-upload" name="csv-file" accept=".csv" onchange="validateCSVFile(this)">
    77             </div>
    78 
    79             <p id="fileError" class="mt-1 fields-error"></p>
    80           </div>
    81 
    82           <div class="divider-light"></div>
    83 
    84           <div class="text-end">
    85             <button type="button" id="cancel_ai_app_plugin_settings_data" role="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-white" name="save-plugin-settings">Cancel</button>
    86             <button type="button" id="save_ai_app_plugin_settings_data" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" name="save-plugin-settings" onclick="ai_app_onsite_save_plugin_settings()">Save</button>
    87           </div>
    88 
    89         </form>
    90       </div>
    91       <div id="Plugin-settings-msg-container" class="message-container"></div>
    92 
    93 
    94 <?php
     173                        <div class="button-wrap">
     174                            <button type="button" role="button" id="cancel" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border  border-2 bg-white" name="save-plugin-settings" data-bs-dismiss="modal" data-role="cancel" >Cancel</button>
     175                            <button type="button" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-6 px-16 border border-2  bg-purple text-white" name="active-plugin-licence-key" id="active_ai_app_plugin_license_key" onclick="ai_app_onsite_app_verify_license_key()">Save</button>
     176                        </div>
     177                    </form>
     178                    <div id="app-license-msg-container" class="message-container mb-3"></div>
     179                </div>
     180            </div>
     181        </div>
     182    </div>
     183    <!-- Modal -->
     184    <div id="Plugin-settings-msg-container" class="message-container"></div>
     185  </div>
     186<?php
    95187      return ob_get_clean();
    96188    }
  • ai-app-onsite/trunk/includes/class-ai-app-onsite-prompt-editor.php

    r3272415 r3307540  
    1010            <!--  Render your plugin setting tab page here -->
    1111            <div>
    12                 <form class="form-wrapper">
     12                <form class="form-wrapper" id="prompt-editor">
    1313                    <?php wp_nonce_field('ai_app_onsite_prompt_editor_nonce', 'ai_app_onsite_prompt_editor_nonce_field'); ?>
    1414                    <div class="form-group">
     
    5252                                <div class="text-end">
    5353                                    <button type="button" class="btn fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-white" name="save-plugin-settings">Cancel</button>
    54                                     <button type="button" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" onclick="ai_app_onsite_save_prompt_editor()">Save</button>
     54                                    <button type="button" class="btn btn-purple fs-medium-14 neutral-black-fs-60 radius-6 py-8 px-16 border border-2 bg-purple-100 text-white" id="ai_app_onsite_save_prompt_editor" onclick="ai_app_onsite_save_prompt_editor()">Save</button>
    5555                                </div>
    5656                            </div>
  • ai-app-onsite/trunk/readme.md

    r3281194 r3307540  
    55**Requires at least:** 4.7 
    66**Tested up to:** 6.7.1 
    7 **Stable tag:** 1.2.2
     7**Stable tag:** 1.2.3
    88**Requires PHP:** 7.0 
    99**License:** GPLv2 or later 
     
    7575- Initial release.
    7676### 1.1.0
    77 - Improved script loading with conditional checks
    78 - Enhanced Bootstrap conflict prevention
    79 - Added settings link in plugins list
    80 - Improved performance and stability
    81 - Fixed CSS issues
    82 - Added auto-open last active tab feature
    83 ### 1.1.1
    84 - Updated plugin settings parameters for improved compatibility
    85 - Fixed getplugindata function for better performance
     77- CSS issue & Auto open last tab
     78
     79### 1.4.3
     80- Fix - the app would not load correctly on some themes.
     81- Update - Improved compatibility with the latest WordPress version.
     82- Update - Updated the plugin to use the latest API endpoints for better performance and reliability.
     83- Fix - Enhanced the user interface for easier navigation and app creation.
     84- Fix - Improved error handling and user feedback during app interactions.
     85- Add - Added a new feature to allow users to save and create multiple app configurations for easier management.
     86- Update - Enhanced security measures to protect user data and API keys.
     87- Add - Added a new feature to allow users to preview their app with shadow and border-radius settings before publishing.
     88- Update - Field selector now supports more number of fields.
     89
    8690
    8791---
    8892
    8993## Upgrade Notice
    90 
    91 ### 1.1.1
    92 - Upgrade to version 1.1.1 for improved plugin settings compatibility and better performance.
    93 
    94 ### 1.1.0
    95 - Upgrade to version 1.1.0 for improved performance, stability, and enhanced Bootstrap compatibility.
    9694
    9795### 1.0.0
  • ai-app-onsite/trunk/readme.txt

    r3281194 r3307540  
    44Requires at least: 4.7
    55Tested up to: 6.7.1
    6 Stable tag: 1.2.2
     6Stable tag: 1.2.3
    77Requires PHP: 7.0
    88License: GPLv2 or later
     
    5656== Changelog ==
    5757
     58= 1.2.3 =
     59* Fix - Resolved app loading issues on certain themes
     60* Update - Improved compatibility with the latest WordPress version
     61* Update - Enhanced API endpoints for better performance and reliability
     62* Fix - Improved user interface for easier navigation and app creation
     63* Fix - Enhanced error handling and user feedback during app interactions
     64* Add - New feature to save and manage multiple app configurations
     65* Update - Enhanced security measures to protect user data and API keys
     66* Add - Preview feature with shadow and border-radius settings
     67* Update - Expanded field selector support
     68
    5869= 1.2.2 =
    5970* Added ability for users to like or dislike a response from the app
     
    7586== Upgrade Notice ==
    7687
     88= 1.2.3 =
     89* Upgrade to version 1.2.3 for improved theme compatibility, enhanced user interface, better error handling, multiple app configurations, and expanded field selector support.
     90
    7791= 1.2.2 =
    7892* Upgrade to version 1.2.2 to allow users to like or dislike responses from the app.
     
    87101* First release of AI App Onsite, offering AI-powered app creation and customization within WordPress.
    88102
     103
    89104== External Services ==
    90105
Note: See TracChangeset for help on using the changeset viewer.