Plugin Directory

Changeset 3414259


Ignore:
Timestamp:
12/08/2025 12:12:46 PM (2 months ago)
Author:
2wstechnologies
Message:

add the feedback send button + note for each ai answer, fix design bug in community page

Location:
askeet/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • askeet/trunk/README.txt

    r3395155 r3414259  
    55Requires at least: 6.2
    66Tested up to: 6.8
    7 Stable tag: 2.0
     7Stable tag: 2.5
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    234234  * NEW: Add annual payment and date renew
    235235
     236= 2.5 =
     237  * NEW: Star rating system (1-5 stars) for AI responses - Rate response quality with a simple click
     238  * NEW: Feedback modal with thumbs up/down and optional comments - Share your experience directly from the plugin
     239  * NEW: Smart auto-retry system - Automatically retries up to 5 times silently before showing error
     240  * NEW: AI disclaimer notice displayed with error messages to remind users to verify responses
     241  * NEW: "Send Feedback" button in header for quick access
     242  * IMPROVED: Modern animated modals for success/error notifications (glassmorphism design)
     243  * FIXED: Discord, Slack, and Contact button text visibility (white text on gradient background)
     244  * FIXED: Subscription upgrade now correctly modifies existing subscription instead of creating duplicates
     245  * IMPROVED: Full internationalization (i18n) support for all new UI elements
     246  * IMPROVED: Better error handling with user-friendly messages
     247
    236248= Upcoming Features =
    237249
     
    258270  * UPDATED: UI/UX design optimization with better responsiveness
    259271
     272= 2.5 =
     273Major update with user feedback features! Rate AI responses with stars, send feedback directly from the plugin, and enjoy improved reliability with smart auto-retry. Fixed button visibility issues and subscription upgrade bugs.
     274
    260275== Source Code ==
    261276The unminified source code for JavaScript and CSS is included in the /assets/js/ and /assets/css/ folders of this plugin. If you use a minified file, the original source is present alongside it.
  • askeet/trunk/askeet.php

    r3395869 r3414259  
    33 * Plugin Name: Askeet
    44 * Description: Askeet is your AI assistant for WooCommerce: generate, run, and analyze SQL queries securely, no code needed, with a modern multilingual interface.
    5  * Version: 2.0
     5 * Version: 2.5
    66 * Author: Reach Technologies
    77 * License: GPL-2.0+
     
    3737        update_option('askeet_install_id', wp_generate_password(24, false));
    3838    }
    39     define('ASKEET_VERSION', '2.0');
     39    define('ASKEET_VERSION', '2.5');
    4040
    4141    $install_id = get_option('askeet_install_id');
     
    179179            'install_id' => $install_id,
    180180            'current_plan' => $current_plan,
     181            'api_url' => askeet_get_api_url(),
     182            'site_url' => get_site_url(),
    181183        ));
    182184        wp_localize_script('askeet-script', 'askeet_query_assistant_i18n', array(
     
    215217            'ai_fixing_query' => __('The assistant is trying to fix the query...', 'askeet'),
    216218            'ai_failed_3_times' => __('Unable to process the request, please reformulate your question.', 'askeet'),
     219            'ai_failed_5_times' => __('Unable to process the request after 5 attempts, please reformulate your question.', 'askeet'),
     220            'ai_retrying' => __('Retrying...', 'askeet'),
     221            'ai_warning' => __('Askeet is an AI and may make mistakes. Please verify the responses.', 'askeet'),
    217222            'no_result_found' => __('No result found.', 'askeet'),
     223            // Star Rating System
     224            'rate_response' => __('Rate this response:', 'askeet'),
     225            'rating_sending' => __('Sending...', 'askeet'),
     226            'rating_thank_you' => __('Thank you!', 'askeet'),
     227            'rating_error' => __('Error', 'askeet'),
     228            // Feedback Modal
     229            'submit_feedback' => __('Submit Feedback', 'askeet'),
     230            'feedback_title' => __('Send Feedback', 'askeet'),
     231            'feedback_subtitle' => __('Help us improve Askeet', 'askeet'),
     232            'satisfied_option' => __('Satisfied', 'askeet'),
     233            'not_satisfied_option' => __('Not Satisfied', 'askeet'),
     234            'feedback_placeholder' => __('Tell us more about your experience... (optional)', 'askeet'),
     235            'feedback_success' => __('Thank you! Your feedback has been submitted.', 'askeet'),
     236            'feedback_error' => __('Error submitting feedback. Please try again.', 'askeet'),
    218237            'already_unlimited' => __('You are already on the unlimited plan.', 'askeet'),
    219238            'upgrade_success' => __('Upgrade successful!', 'askeet'),
     
    258277        ?>
    259278        <div class="wrap askeet">
    260             <div class="wcqa-header-flex">
     279            <div class="wcqa-header-flex" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
    261280                <img src="<?php echo esc_url(plugins_url('assets/img/askeet-logo.svg', __FILE__)); ?>" alt="<?php esc_attr_e('Ask Woo Logo', 'askeet'); ?>">
    262             </div>
     281                <button type="button" id="askeet-feedback-btn" class="askeet-feedback-trigger">
     282                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     283                        <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
     284                    </svg>
     285                    <?php echo esc_html__('Send Feedback', 'askeet'); ?>
     286                </button>
     287            </div>
     288
     289            <!-- Feedback Modal -->
     290            <div id="askeet-feedback-overlay" class="askeet-feedback-overlay">
     291                <div class="askeet-feedback-modal">
     292                    <div class="askeet-feedback-header">
     293                        <h2><?php echo esc_html__('Send your Feedback', 'askeet'); ?></h2>
     294                        <button type="button" class="askeet-feedback-close" id="askeet-feedback-close">
     295                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     296                                <path d="M18 6l-12 12"></path>
     297                                <path d="M6 6l12 12"></path>
     298                            </svg>
     299                        </button>
     300                    </div>
     301
     302                    <div class="askeet-feedback-body">
     303                        <!-- Info box -->
     304                        <div class="askeet-feedback-info">
     305                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     306                                <path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"></path>
     307                                <path d="M12 9h.01"></path>
     308                                <path d="M11 12h1v4h1"></path>
     309                            </svg>
     310                            <p><?php echo esc_html__('Your feedback helps us improve Askeet for everyone!', 'askeet'); ?></p>
     311                            <a href="https://calendly.com/askeet/feedback" target="_blank" rel="noopener noreferrer" class="askeet-feedback-book">
     312                                <?php echo esc_html__('Book a call', 'askeet'); ?>
     313                            </a>
     314                        </div>
     315
     316                        <!-- Satisfaction question -->
     317                        <div class="askeet-feedback-satisfaction">
     318                            <p class="askeet-feedback-question"><?php echo esc_html__('Are you satisfied with your experience?', 'askeet'); ?></p>
     319                            <div class="askeet-feedback-thumbs">
     320                                <button type="button" class="askeet-thumb-btn" data-satisfaction="yes" id="thumb-yes">
     321                                    <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     322                                        <path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path>
     323                                    </svg>
     324                                    <span><?php echo esc_html__('Yes', 'askeet'); ?></span>
     325                                </button>
     326                                <button type="button" class="askeet-thumb-btn" data-satisfaction="no" id="thumb-no">
     327                                    <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     328                                        <path d="M7 13v-8a1 1 0 0 0 -1 -1h-2a1 1 0 0 0 -1 1v7a1 1 0 0 0 1 1h3a4 4 0 0 1 4 4v1a2 2 0 0 0 4 0v-5h3a2 2 0 0 0 2 -2l-1 -5a2 3 0 0 0 -2 -2h-7a3 3 0 0 0 -3 3"></path>
     329                                    </svg>
     330                                    <span><?php echo esc_html__('No', 'askeet'); ?></span>
     331                                </button>
     332                            </div>
     333                        </div>
     334
     335                        <!-- Feedback textarea -->
     336                        <div class="askeet-feedback-textarea-wrapper">
     337                            <textarea id="askeet-feedback-text" class="askeet-feedback-textarea" placeholder="<?php echo esc_attr__('Type your feedback (Optional)', 'askeet'); ?>" maxlength="500"></textarea>
     338                            <div class="askeet-feedback-counter">
     339                                <span id="askeet-char-count">0</span>/500
     340                            </div>
     341                        </div>
     342                    </div>
     343
     344                    <div class="askeet-feedback-footer">
     345                        <button type="button" id="askeet-submit-feedback" class="askeet-submit-btn" disabled>
     346                            <?php echo esc_html__('Submit Feedback', 'askeet'); ?>
     347                        </button>
     348                        <button type="button" id="askeet-cancel-feedback" class="askeet-cancel-btn">
     349                            <?php echo esc_html__('Cancel', 'askeet'); ?>
     350                        </button>
     351                    </div>
     352                </div>
     353            </div>
     354
    263355            <div id="wcqa-chat-container">
    264356              <div id="wcqa-messages"></div>
     
    323415            $previous_ai_response = isset($data['previous_ai_response']) ? sanitize_textarea_field(wp_unslash($data['previous_ai_response'])) : '';
    324416            $install_id = isset($data['install_id']) ? sanitize_text_field(wp_unslash($data['install_id'])) : '';
     417            $failed_sql_query = isset($data['failed_sql_query']) ? sanitize_textarea_field(wp_unslash($data['failed_sql_query'])) : '';
     418            $sql_error = isset($data['sql_error']) ? sanitize_textarea_field(wp_unslash($data['sql_error'])) : '';
     419            $retry_attempt = isset($data['retry_attempt']) ? intval($data['retry_attempt']) : 0;
    325420        } else {
    326421            $query_request = isset($_POST['query_request']) ? sanitize_textarea_field(wp_unslash($_POST['query_request'])) : '';
     
    328423            $previous_ai_response = isset($_POST['previous_ai_response']) ? sanitize_textarea_field(wp_unslash($_POST['previous_ai_response'])) : '';
    329424            $install_id = isset($_POST['install_id']) ? sanitize_text_field(wp_unslash($_POST['install_id'])) : '';
     425            $failed_sql_query = isset($_POST['failed_sql_query']) ? sanitize_textarea_field(wp_unslash($_POST['failed_sql_query'])) : '';
     426            $sql_error = isset($_POST['sql_error']) ? sanitize_textarea_field(wp_unslash($_POST['sql_error'])) : '';
     427            $retry_attempt = isset($_POST['retry_attempt']) ? intval($_POST['retry_attempt']) : 0;
    330428        }
    331429        $db_structure = $this->askeet_get_full_db_structure();
     
    338436            'previous_ai_response' => $previous_ai_response,
    339437            'install_id' => $install_id,
     438            'failed_sql_query' => $failed_sql_query,
     439            'sql_error' => $sql_error,
     440            'retry_attempt' => $retry_attempt,
    340441        );
    341442        $response = wp_remote_post($full_url, array(
  • askeet/trunk/assets/css/style.css

    r3395155 r3414259  
    4545}
    4646
    47 .btn {
    48   color: white;
     47.btn,
     48a.btn,
     49.button-group a.btn,
     50.button-group .btn {
     51  color: #ffffff !important;
    4952  border: none;
    5053  padding: 17px 34px;
     
    5457  text-align: center;
    5558  min-width: 160px;
    56   transition: background 0.3s ease;
     59  transition: all 0.3s ease;
    5760  border-radius: 10px;
    58   background: linear-gradient(180deg, rgb(61 63 66 / 74%) 0%, #13191e 100%);
    59 }
    60 
    61 .btn:hover {
    62   background: #004d8c;
    63   color: white!important;
     61  background: linear-gradient(135deg, #005a9e 0%, #0077cc 100%) !important;
     62  text-decoration: none !important;
     63  display: inline-flex;
     64  align-items: center;
     65  justify-content: center;
     66  box-shadow: 0 4px 12px rgba(0, 90, 158, 0.25);
     67}
     68
     69.btn:hover,
     70a.btn:hover,
     71.button-group a.btn:hover,
     72.button-group .btn:hover {
     73  background: linear-gradient(135deg, #004080 0%, #005fa3 100%) !important;
     74  color: #ffffff !important;
     75  text-decoration: none !important;
     76  transform: translateY(-2px);
     77  box-shadow: 0 6px 20px rgba(0, 90, 158, 0.35);
     78}
     79
     80.btn:focus,
     81a.btn:focus {
     82  outline: none;
     83  box-shadow: 0 0 0 3px rgba(0, 90, 158, 0.3);
     84}
     85
     86#btn-discord,
     87#btn-slack,
     88#btn-support {
     89  color: #ffffff !important;
     90  background: linear-gradient(135deg, #005a9e 0%, #0077cc 100%) !important;
    6491}
    6592
     
    11951222    }
    11961223}
     1224
     1225/* ========================================
     1226   FEEDBACK MODAL STYLES
     1227   ======================================== */
     1228
     1229/* Feedback Trigger Button */
     1230.askeet-feedback-trigger {
     1231    display: inline-flex;
     1232    align-items: center;
     1233    gap: 8px;
     1234    padding: 10px 20px;
     1235    background: linear-gradient(135deg, #005a9e 0%, #0077cc 100%);
     1236    color: #ffffff !important;
     1237    border: none;
     1238    border-radius: 10px;
     1239    font-size: 14px;
     1240    font-weight: 600;
     1241    cursor: pointer;
     1242    transition: all 0.3s ease;
     1243    box-shadow: 0 4px 12px rgba(0, 90, 158, 0.25);
     1244}
     1245
     1246.askeet-feedback-trigger:hover {
     1247    background: linear-gradient(135deg, #004080 0%, #005fa3 100%);
     1248    transform: translateY(-2px);
     1249    box-shadow: 0 6px 20px rgba(0, 90, 158, 0.35);
     1250}
     1251
     1252.askeet-feedback-trigger svg {
     1253    stroke: #ffffff;
     1254}
     1255
     1256/* Feedback Modal Overlay */
     1257.askeet-feedback-overlay {
     1258    display: none;
     1259    position: fixed;
     1260    top: 0;
     1261    left: 0;
     1262    width: 100%;
     1263    height: 100%;
     1264    background: rgba(0, 0, 0, 0.6);
     1265    backdrop-filter: blur(8px);
     1266    z-index: 999999;
     1267    opacity: 0;
     1268    transition: opacity 0.3s ease;
     1269}
     1270
     1271.askeet-feedback-overlay.active {
     1272    display: flex;
     1273    align-items: center;
     1274    justify-content: center;
     1275    opacity: 1;
     1276}
     1277
     1278/* Feedback Modal */
     1279.askeet-feedback-modal {
     1280    background: #ffffff;
     1281    border-radius: 16px;
     1282    max-width: 520px;
     1283    width: 90%;
     1284    box-shadow: 0 25px 80px rgba(0, 0, 0, 0.25);
     1285    transform: scale(0.9) translateY(20px);
     1286    opacity: 0;
     1287    transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
     1288    overflow: hidden;
     1289}
     1290
     1291.askeet-feedback-overlay.active .askeet-feedback-modal {
     1292    transform: scale(1) translateY(0);
     1293    opacity: 1;
     1294}
     1295
     1296/* Modal Header */
     1297.askeet-feedback-header {
     1298    position: relative;
     1299    padding: 24px 24px 16px;
     1300    border-bottom: 1px solid #f0f0f0;
     1301}
     1302
     1303.askeet-feedback-header h2 {
     1304    font-size: 24px;
     1305    font-weight: 700;
     1306    color: #1a1a1a;
     1307    margin: 0;
     1308    text-align: center;
     1309}
     1310
     1311.askeet-feedback-close {
     1312    position: absolute;
     1313    top: 20px;
     1314    right: 20px;
     1315    background: transparent;
     1316    border: none;
     1317    padding: 8px;
     1318    cursor: pointer;
     1319    border-radius: 50%;
     1320    transition: background 0.2s ease;
     1321}
     1322
     1323.askeet-feedback-close:hover {
     1324    background: #f0f0f0;
     1325}
     1326
     1327.askeet-feedback-close svg {
     1328    stroke: #666;
     1329    display: block;
     1330}
     1331
     1332/* Modal Body */
     1333.askeet-feedback-body {
     1334    padding: 24px;
     1335}
     1336
     1337/* Info Box */
     1338.askeet-feedback-info {
     1339    display: flex;
     1340    align-items: center;
     1341    gap: 12px;
     1342    padding: 14px 16px;
     1343    background: linear-gradient(135deg, #e8f4fd 0%, #f0f7ff 100%);
     1344    border-radius: 10px;
     1345    margin-bottom: 24px;
     1346}
     1347
     1348.askeet-feedback-info svg {
     1349    flex-shrink: 0;
     1350    stroke: #005a9e;
     1351}
     1352
     1353.askeet-feedback-info p {
     1354    font-size: 14px;
     1355    color: #333;
     1356    margin: 0;
     1357    flex-grow: 1;
     1358}
     1359
     1360.askeet-feedback-book {
     1361    color: #005a9e !important;
     1362    font-size: 14px;
     1363    font-weight: 600;
     1364    text-decoration: none;
     1365    white-space: nowrap;
     1366    border-bottom: 1px solid transparent;
     1367    transition: border-color 0.2s ease;
     1368}
     1369
     1370.askeet-feedback-book:hover {
     1371    border-bottom-color: #005a9e;
     1372    text-decoration: none !important;
     1373}
     1374
     1375/* Satisfaction Section */
     1376.askeet-feedback-satisfaction {
     1377    text-align: center;
     1378    margin-bottom: 24px;
     1379}
     1380
     1381.askeet-feedback-question {
     1382    font-size: 14px;
     1383    color: #666;
     1384    margin-bottom: 16px;
     1385}
     1386
     1387.askeet-feedback-thumbs {
     1388    display: flex;
     1389    justify-content: center;
     1390    gap: 16px;
     1391}
     1392
     1393.askeet-thumb-btn {
     1394    display: flex;
     1395    align-items: center;
     1396    gap: 10px;
     1397    padding: 12px 24px;
     1398    background: transparent;
     1399    border: 2px solid #e0e0e0;
     1400    border-radius: 12px;
     1401    cursor: pointer;
     1402    transition: all 0.2s ease;
     1403    min-width: 100px;
     1404    justify-content: center;
     1405}
     1406
     1407.askeet-thumb-btn:hover {
     1408    border-color: #bbb;
     1409    background: #fafafa;
     1410}
     1411
     1412.askeet-thumb-btn.selected {
     1413    border-color: #005a9e;
     1414    background: linear-gradient(135deg, #e8f4fd 0%, #f0f7ff 100%);
     1415}
     1416
     1417.askeet-thumb-btn.selected svg {
     1418    stroke: #005a9e;
     1419}
     1420
     1421.askeet-thumb-btn svg {
     1422    stroke: #888;
     1423    transition: stroke 0.2s ease;
     1424}
     1425
     1426.askeet-thumb-btn span {
     1427    font-size: 15px;
     1428    font-weight: 600;
     1429    color: #333;
     1430}
     1431
     1432/* Textarea */
     1433.askeet-feedback-textarea-wrapper {
     1434    position: relative;
     1435}
     1436
     1437.askeet-feedback-textarea {
     1438    width: 100%;
     1439    height: 120px;
     1440    padding: 14px 16px;
     1441    border: 1px solid #e0e0e0;
     1442    border-radius: 10px;
     1443    font-size: 14px;
     1444    font-family: inherit;
     1445    resize: none;
     1446    outline: none;
     1447    transition: border-color 0.2s ease, box-shadow 0.2s ease;
     1448    box-sizing: border-box;
     1449}
     1450
     1451.askeet-feedback-textarea:focus {
     1452    border-color: #005a9e;
     1453    box-shadow: 0 0 0 3px rgba(0, 90, 158, 0.15);
     1454}
     1455
     1456.askeet-feedback-textarea::placeholder {
     1457    color: #aaa;
     1458}
     1459
     1460.askeet-feedback-counter {
     1461    text-align: right;
     1462    margin-top: 8px;
     1463    font-size: 12px;
     1464    color: #999;
     1465}
     1466
     1467/* Modal Footer */
     1468.askeet-feedback-footer {
     1469    padding: 20px 24px 24px;
     1470    border-top: 1px solid #f0f0f0;
     1471    display: flex;
     1472    flex-direction: column;
     1473    gap: 12px;
     1474    align-items: center;
     1475}
     1476
     1477.askeet-submit-btn {
     1478    display: inline-flex;
     1479    align-items: center;
     1480    justify-content: center;
     1481    padding: 14px 40px;
     1482    background: linear-gradient(135deg, #005a9e 0%, #0077cc 100%);
     1483    color: #ffffff !important;
     1484    border: none;
     1485    border-radius: 10px;
     1486    font-size: 16px;
     1487    font-weight: 600;
     1488    cursor: pointer;
     1489    transition: all 0.3s ease;
     1490    box-shadow: 0 8px 20px rgba(0, 90, 158, 0.3);
     1491    min-width: 180px;
     1492}
     1493
     1494.askeet-submit-btn:hover:not(:disabled) {
     1495    background: linear-gradient(135deg, #004080 0%, #005fa3 100%);
     1496    transform: translateY(-2px);
     1497    box-shadow: 0 10px 25px rgba(0, 90, 158, 0.4);
     1498}
     1499
     1500.askeet-submit-btn:disabled {
     1501    background: #e0e0e0;
     1502    color: #999 !important;
     1503    cursor: not-allowed;
     1504    box-shadow: none;
     1505    opacity: 0.7;
     1506}
     1507
     1508.askeet-cancel-btn {
     1509    background: transparent;
     1510    border: none;
     1511    color: #666 !important;
     1512    font-size: 15px;
     1513    font-weight: 500;
     1514    cursor: pointer;
     1515    padding: 8px 16px;
     1516    transition: color 0.2s ease;
     1517}
     1518
     1519.askeet-cancel-btn:hover {
     1520    color: #333 !important;
     1521}
     1522
     1523/* Responsive */
     1524@media (max-width: 480px) {
     1525    .askeet-feedback-modal {
     1526        width: 95%;
     1527        max-width: none;
     1528    }
     1529
     1530    .askeet-feedback-header {
     1531        padding: 20px 16px 14px;
     1532    }
     1533
     1534    .askeet-feedback-header h2 {
     1535        font-size: 20px;
     1536    }
     1537
     1538    .askeet-feedback-body {
     1539        padding: 16px;
     1540    }
     1541
     1542    .askeet-feedback-info {
     1543        flex-direction: column;
     1544        text-align: center;
     1545    }
     1546
     1547    .askeet-feedback-thumbs {
     1548        flex-direction: column;
     1549        gap: 10px;
     1550    }
     1551
     1552    .askeet-thumb-btn {
     1553        width: 100%;
     1554    }
     1555}
     1556
     1557/* ========================================
     1558   RETRY CONTAINER & AI WARNING STYLES
     1559   ======================================== */
     1560
     1561.wcqa-retry-container {
     1562    display: flex;
     1563    flex-wrap: wrap;
     1564    align-items: center;
     1565    gap: 12px;
     1566    margin-top: 12px;
     1567    padding: 12px 16px;
     1568    background: linear-gradient(135deg, rgba(255, 193, 7, 0.1) 0%, rgba(255, 152, 0, 0.08) 100%);
     1569    border-radius: 10px;
     1570    border: 1px solid rgba(255, 193, 7, 0.3);
     1571}
     1572
     1573.wcqa-retry-query.button {
     1574    background: linear-gradient(135deg, #ff6b6b 0%, #ee5a5a 100%) !important;
     1575    color: #fff !important;
     1576    border: none !important;
     1577    padding: 8px 18px !important;
     1578    border-radius: 8px !important;
     1579    font-weight: 600 !important;
     1580    font-size: 13px !important;
     1581    cursor: pointer !important;
     1582    transition: all 0.3s ease !important;
     1583    box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3) !important;
     1584}
     1585
     1586.wcqa-retry-query.button:hover {
     1587    background: linear-gradient(135deg, #ff5252 0%, #e53935 100%) !important;
     1588    transform: translateY(-1px) !important;
     1589    box-shadow: 0 4px 12px rgba(255, 107, 107, 0.4) !important;
     1590}
     1591
     1592.wcqa-ai-warning {
     1593    display: flex;
     1594    align-items: center;
     1595    gap: 8px;
     1596    font-size: 12.5px;
     1597    color: #856404;
     1598    font-style: italic;
     1599    line-height: 1.4;
     1600    flex: 1;
     1601    min-width: 200px;
     1602}
     1603
     1604.wcqa-ai-warning::before {
     1605    content: "\26A0";
     1606    font-size: 16px;
     1607    color: #f39c12;
     1608    font-style: normal;
     1609}
     1610
     1611@media (max-width: 600px) {
     1612    .wcqa-retry-container {
     1613        flex-direction: column;
     1614        align-items: flex-start;
     1615    }
     1616
     1617    .wcqa-ai-warning {
     1618        min-width: unset;
     1619    }
     1620}
     1621
     1622/* ========================================
     1623   STAR RATING SYSTEM STYLES
     1624   ======================================== */
     1625
     1626.wcqa-rating-container {
     1627    display: flex;
     1628    flex-wrap: wrap;
     1629    align-items: center;
     1630    gap: 10px;
     1631    margin-top: 14px;
     1632    padding: 10px 14px;
     1633    background: linear-gradient(135deg, rgba(255, 215, 0, 0.08) 0%, rgba(255, 193, 7, 0.05) 100%);
     1634    border-radius: 10px;
     1635    border: 1px solid rgba(255, 193, 7, 0.2);
     1636}
     1637
     1638.wcqa-rating-label {
     1639    font-size: 12.5px;
     1640    color: #666;
     1641    font-weight: 500;
     1642}
     1643
     1644.wcqa-stars-container {
     1645    display: flex;
     1646    gap: 4px;
     1647    cursor: pointer;
     1648}
     1649
     1650.wcqa-stars-container.rated {
     1651    cursor: default;
     1652}
     1653
     1654.wcqa-star {
     1655    font-size: 22px;
     1656    color: #ddd;
     1657    transition: all 0.2s ease;
     1658    line-height: 1;
     1659}
     1660
     1661.wcqa-star:hover,
     1662.wcqa-star.hovered {
     1663    color: #ffc107;
     1664    transform: scale(1.15);
     1665}
     1666
     1667.wcqa-star.selected {
     1668    color: #ffc107;
     1669}
     1670
     1671.wcqa-stars-container.rated .wcqa-star {
     1672    cursor: default;
     1673}
     1674
     1675.wcqa-stars-container.rated .wcqa-star:hover {
     1676    transform: none;
     1677}
     1678
     1679.wcqa-rating-feedback {
     1680    font-size: 12px;
     1681    color: #888;
     1682    font-style: italic;
     1683    transition: all 0.3s ease;
     1684}
     1685
     1686.wcqa-rating-feedback.success {
     1687    color: #4caf50;
     1688    font-weight: 600;
     1689}
     1690
     1691.wcqa-rating-feedback.error {
     1692    color: #f44336;
     1693}
     1694
     1695@media (max-width: 500px) {
     1696    .wcqa-rating-container {
     1697        flex-direction: column;
     1698        align-items: flex-start;
     1699    }
     1700}
  • askeet/trunk/assets/js/script.js

    r3395155 r3414259  
    2424    function renderMessage(content, sender = 'ai', queryText = null, isError = false) {
    2525        const msg = $('<div class="wcqa-message"></div>').addClass(sender);
     26        const messageId = 'msg-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
     27        msg.attr('data-message-id', messageId);
     28
    2629        if (sender === 'user' && queryText) {
    2730            // Add Save button for user queries
     
    4144        }
    4245        if (isError) {
     46            // Create a container for retry button and AI warning
     47            const retryContainer = $('<div class="wcqa-retry-container"></div>');
    4348            const retryBtn = $('<button class="wcqa-retry-query button">'+askeet_query_assistant_i18n.retry+'</button>');
    4449            retryBtn.on('click', function(e) {
     
    4651                retryLastQuery();
    4752            });
    48             msg.append(retryBtn);
    49         }
     53            retryContainer.append(retryBtn);
     54            // Add AI warning text
     55            const aiWarning = $('<span class="wcqa-ai-warning">'+(askeet_query_assistant_i18n.ai_warning || 'Askeet est une AI et peut faire des erreurs. Veuillez svp vérifier les réponses.')+'</span>');
     56            retryContainer.append(aiWarning);
     57            msg.append(retryContainer);
     58        }
     59
     60        // Add star rating for AI messages (not for errors)
     61        if (sender === 'ai' && content && !isError) {
     62            const ratingContainer = $('<div class="wcqa-rating-container"></div>');
     63            const ratingLabel = $('<span class="wcqa-rating-label">'+(askeet_query_assistant_i18n.rate_response || 'Rate this response:')+'</span>');
     64            const starsContainer = $('<div class="wcqa-stars-container" data-message-id="'+messageId+'"></div>');
     65
     66            // Create 5 stars
     67            for (let i = 1; i <= 5; i++) {
     68                const star = $('<span class="wcqa-star" data-rating="'+i+'">&#9734;</span>');
     69                starsContainer.append(star);
     70            }
     71
     72            const ratingFeedback = $('<span class="wcqa-rating-feedback"></span>');
     73            ratingContainer.append(ratingLabel);
     74            ratingContainer.append(starsContainer);
     75            ratingContainer.append(ratingFeedback);
     76            msg.append(ratingContainer);
     77        }
     78
    5079        $('#wcqa-messages').append(msg);
    5180        scrollChatToBottom();
     
    194223        $('#process-query').prop('disabled', true);
    195224        let attempt = 0;
     225        const maxAttempts = 5; // Retry up to 5 times before showing error
     226        let lastFailedSqlQuery = ''; // Track failed SQL for retry context
     227        let lastErrorMessage = ''; // Track error message for retry context
     228
    196229        function tryProcessQuery() {
    197230            attempt++;
     231            // Build request data with retry context if this is a retry attempt
     232            let requestData = {
     233                action: 'askeet_process_query_request',
     234                query_request: queryRequest,
     235                previous_user_query: previous_user_query,
     236                previous_ai_response: previous_ai_response,
     237                nonce: askeet_query_assistant.nonce,
     238                install_id: askeet_query_assistant.install_id
     239            };
     240
     241            // If this is a retry, include the failed query and error for AI context
     242            if (attempt > 1 && lastFailedSqlQuery) {
     243                requestData.failed_sql_query = lastFailedSqlQuery;
     244                requestData.sql_error = lastErrorMessage;
     245                requestData.retry_attempt = attempt;
     246            }
     247
    198248            $.ajax({
    199249                url: askeet_query_assistant.ajax_url,
    200250                type: 'POST',
    201                 data: {
    202                     action: 'askeet_process_query_request',
    203                     query_request: queryRequest,
    204                     previous_user_query: previous_user_query,
    205                     previous_ai_response: previous_ai_response,
    206                     nonce: askeet_query_assistant.nonce,
    207                     install_id: askeet_query_assistant.install_id
    208                 },
     251                data: requestData,
    209252                success: function(response) {
    210                     // // console.log('[WCQA] AJAX success:', response); // Debug log
    211                     removeLoading();
    212                     $('#process-query').prop('disabled', false);
    213253                    // PATCH: Detect limit errors in AJAX responses and show only one modal
    214                     if (handleLimitModals(response)) return;
     254                    if (handleLimitModals(response)) {
     255                        removeLoading();
     256                        $('#process-query').prop('disabled', false);
     257                        return;
     258                    }
    215259                    let results = (response.data && response.data.results) ? response.data.results : (response.results ? response.results : []);
    216260                    let sql_query = (response.data && response.data.sql_query) ? response.data.sql_query : (response.sql_query ? response.sql_query : '');
     
    218262                    // Only execute the paginated query, do NOT render SQL or button
    219263                    if (response.success && currentSqlQuery) {
    220                         // TODO: Hide this in production
     264                        removeLoading();
     265                        $('#process-query').prop('disabled', false);
    221266                        clearResultMessages();
    222267                        executeQueryPage(1, true);
    223268                    } else if (response.success) {
     269                        removeLoading();
     270                        $('#process-query').prop('disabled', false);
    224271                        clearResultMessages();
    225272                        renderMessage('<div class="query-success">'+askeet_query_assistant_i18n.query_executed_successfully+' 0 '+askeet_query_assistant_i18n.results_found+'.</div>', 'ai');
    226273                    } else {
    227                         clearErrorMessages();
    228                         let errorMsg = '';
    229                         if (response.data && response.data.message && response.data.message.indexOf('Seules les requêtes SELECT sont autorisées') !== -1) {
    230                             errorMsg = response.data.message;
     274                        // Store failed query info for next retry
     275                        if (sql_query) {
     276                            lastFailedSqlQuery = sql_query;
     277                        }
     278                        lastErrorMessage = (response.data && response.data.message) ? response.data.message : 'Query failed';
     279
     280                        // Check if it's a non-retryable error (like security restriction)
     281                        let isNonRetryableError = response.data && response.data.message &&
     282                            response.data.message.indexOf('Seules les requêtes SELECT sont autorisées') !== -1;
     283
     284                        if (isNonRetryableError || attempt >= maxAttempts) {
     285                            // Max attempts reached or non-retryable error, show final error
     286                            removeLoading();
     287                            $('#process-query').prop('disabled', false);
     288                            clearErrorMessages();
     289                            let errorMsg = '';
     290                            if (isNonRetryableError) {
     291                                errorMsg = response.data.message;
     292                            } else {
     293                                errorMsg = askeet_query_assistant_i18n.ai_failed_5_times || askeet_query_assistant_i18n.ai_failed_3_times;
     294                            }
     295                            renderMessage('<div class="query-error">'+errorMsg+'</div>', 'ai', null, true);
    231296                        } else {
    232                             errorMsg = askeet_query_assistant_i18n.ai_failed_3_times;
     297                            // Auto-retry silently in background with context
     298                            setTimeout(tryProcessQuery, 1000);
    233299                        }
    234                         renderMessage('<div class="query-error">'+errorMsg+'</div>', 'ai', null, true);
    235300                    }
    236301                },
    237302                error: function(xhr, status, error) {
    238                     removeLoading();
    239                     clearErrorMessages();
     303                    // Store error for retry context
     304                    lastErrorMessage = error || 'Network error';
     305
    240306                    try {
    241307                        var resp = xhr.responseJSON || JSON.parse(xhr.responseText);
    242                         // console.log('[WCQA] AJAX error:', resp); // Debug log
    243308                        // PATCH: Detect limit errors in AJAX responses and show only one modal
    244                         if (handleLimitModals(resp)) return;
    245                     } catch(e){
    246                         // console.log('[WCQA] AJAX error (parse fail):', xhr.responseText);
     309                        if (handleLimitModals(resp)) {
     310                            removeLoading();
     311                            $('#process-query').prop('disabled', false);
     312                            return;
     313                        }
     314                        if (resp && resp.data && resp.data.message) {
     315                            lastErrorMessage = resp.data.message;
     316                        }
     317                    } catch(e){}
     318
     319                    if (attempt >= maxAttempts) {
     320                        // Max attempts reached, show final error
     321                        removeLoading();
     322                        $('#process-query').prop('disabled', false);
     323                        clearErrorMessages();
     324                        renderMessage('<div class="query-error">'+askeet_query_assistant_i18n.error_communication+'</div>', 'ai', null, true);
     325                    } else {
     326                        // Auto-retry silently in background
     327                        setTimeout(tryProcessQuery, 1000);
    247328                    }
    248                     renderMessage('<div class="query-error">'+askeet_query_assistant_i18n.error_communication+'</div>', 'ai', null, true);
    249329                }
    250330            });
     
    267347        $('#process-query').prop('disabled', true);
    268348        let attempt = 0;
     349        const maxAttempts = 5; // Retry up to 5 times before showing error
     350        let lastFailedSqlQuery = currentSqlQuery || ''; // Use last known SQL query
     351        let lastErrorMessage = ''; // Track error message for retry context
     352
    269353        function tryProcessQuery() {
    270354            attempt++;
     355            // Build request data with retry context if this is a retry attempt
     356            let requestData = {
     357                action: 'askeet_process_query_request',
     358                query_request: lastQueryRequest,
     359                nonce: askeet_query_assistant.nonce,
     360                install_id: askeet_query_assistant.install_id
     361            };
     362
     363            // If this is a retry, include the failed query and error for AI context
     364            if (attempt > 1 && lastFailedSqlQuery) {
     365                requestData.failed_sql_query = lastFailedSqlQuery;
     366                requestData.sql_error = lastErrorMessage;
     367                requestData.retry_attempt = attempt;
     368            }
     369
    271370            $.ajax({
    272371                url: askeet_query_assistant.ajax_url,
    273372                type: 'POST',
    274                 data: {
    275                     action: 'askeet_process_query_request',
    276                     query_request: lastQueryRequest,
    277                     nonce: askeet_query_assistant.nonce,
    278                     install_id: askeet_query_assistant.install_id
    279                 },
     373                data: requestData,
    280374                success: function(response) {
    281                     // console.log('[WCQA] AJAX success:', response); // Debug log
    282                     removeLoading();
    283                     $('#process-query').prop('disabled', false);
    284375                    // PATCH: Detect limit errors in AJAX responses and show only one modal
    285                     if (handleLimitModals(response)) return;
     376                    if (handleLimitModals(response)) {
     377                        removeLoading();
     378                        $('#process-query').prop('disabled', false);
     379                        return;
     380                    }
    286381                    let results = (response.data && response.data.results) ? response.data.results : (response.results ? response.results : []);
    287382                    let sql_query = (response.data && response.data.sql_query) ? response.data.sql_query : (response.sql_query ? response.sql_query : '');
    288383                    currentSqlQuery = sql_query || '';
    289                     if (response.success && results && results.length > 0) {
     384                    if (response.success && currentSqlQuery) {
     385                        removeLoading();
     386                        $('#process-query').prop('disabled', false);
    290387                        executeQueryPage(1, true);
    291388                    } else if (response.success) {
     389                        removeLoading();
     390                        $('#process-query').prop('disabled', false);
    292391                        renderMessage('<div class="query-success">'+askeet_query_assistant_i18n.query_executed_successfully+' 0 '+askeet_query_assistant_i18n.results_found+'.</div>', 'ai');
    293392                    } else {
    294                         clearErrorMessages();
    295                         let errorMsg = '';
    296                         if (response.data && response.data.message && response.data.message.indexOf('Seules les requêtes SELECT sont autorisées') !== -1) {
    297                             errorMsg = response.data.message;
     393                        // Store failed query info for next retry
     394                        if (sql_query) {
     395                            lastFailedSqlQuery = sql_query;
     396                        }
     397                        lastErrorMessage = (response.data && response.data.message) ? response.data.message : 'Query failed';
     398
     399                        // Check if it's a non-retryable error (like security restriction)
     400                        let isNonRetryableError = response.data && response.data.message &&
     401                            response.data.message.indexOf('Seules les requêtes SELECT sont autorisées') !== -1;
     402
     403                        if (isNonRetryableError || attempt >= maxAttempts) {
     404                            // Max attempts reached or non-retryable error, show final error
     405                            removeLoading();
     406                            $('#process-query').prop('disabled', false);
     407                            clearErrorMessages();
     408                            let errorMsg = '';
     409                            if (isNonRetryableError) {
     410                                errorMsg = response.data.message;
     411                            } else {
     412                                errorMsg = askeet_query_assistant_i18n.ai_failed_5_times || askeet_query_assistant_i18n.ai_failed_3_times;
     413                            }
     414                            renderMessage('<div class="query-error">'+errorMsg+'</div>', 'ai', null, true);
    298415                        } else {
    299                             errorMsg = askeet_query_assistant_i18n.ai_failed_3_times;
     416                            // Auto-retry silently in background with context
     417                            setTimeout(tryProcessQuery, 1000);
    300418                        }
    301                         renderMessage('<div class="query-error">'+errorMsg+'</div>', 'ai', null, true);
    302419                    }
    303420                },
    304421                error: function(xhr, status, error) {
    305                     removeLoading();
    306                     clearErrorMessages();
     422                    // Store error for retry context
     423                    lastErrorMessage = error || 'Network error';
     424
    307425                    try {
    308426                        var resp = xhr.responseJSON || JSON.parse(xhr.responseText);
    309                         // console.log('[WCQA] AJAX error:', resp); // Debug log
    310427                        // PATCH: Detect limit errors in AJAX responses and show only one modal
    311                         if (handleLimitModals(resp)) return;
    312                     } catch(e){
    313                         // console.log('[WCQA] AJAX error (parse fail):', xhr.responseText);
     428                        if (handleLimitModals(resp)) {
     429                            removeLoading();
     430                            $('#process-query').prop('disabled', false);
     431                            return;
     432                        }
     433                        if (resp && resp.data && resp.data.message) {
     434                            lastErrorMessage = resp.data.message;
     435                        }
     436                    } catch(e){}
     437
     438                    if (attempt >= maxAttempts) {
     439                        // Max attempts reached, show final error
     440                        removeLoading();
     441                        $('#process-query').prop('disabled', false);
     442                        clearErrorMessages();
     443                        renderMessage('<div class="query-error">'+askeet_query_assistant_i18n.error_communication+'</div>', 'ai', null, true);
     444                    } else {
     445                        // Auto-retry silently in background
     446                        setTimeout(tryProcessQuery, 1000);
    314447                    }
    315                     renderMessage('<div class="query-error">'+askeet_query_assistant_i18n.error_communication+'</div>', 'ai', null, true);
    316448                }
    317449            });
     
    11341266        });
    11351267    });
     1268
     1269    // ========================================
     1270    // FEEDBACK MODAL SYSTEM
     1271    // ========================================
     1272
     1273    let feedbackSatisfaction = null;
     1274
     1275    // Open feedback modal
     1276    $('#askeet-feedback-btn').on('click', function() {
     1277        $('#askeet-feedback-overlay').addClass('active');
     1278        resetFeedbackModal();
     1279    });
     1280
     1281    // Close feedback modal
     1282    $('#askeet-feedback-close, #askeet-cancel-feedback').on('click', function() {
     1283        closeFeedbackModal();
     1284    });
     1285
     1286    // Close on overlay click
     1287    $('#askeet-feedback-overlay').on('click', function(e) {
     1288        if ($(e.target).is('#askeet-feedback-overlay')) {
     1289            closeFeedbackModal();
     1290        }
     1291    });
     1292
     1293    // Handle thumb buttons
     1294    $('.askeet-thumb-btn').on('click', function() {
     1295        $('.askeet-thumb-btn').removeClass('selected');
     1296        $(this).addClass('selected');
     1297        feedbackSatisfaction = $(this).data('satisfaction');
     1298        updateSubmitButton();
     1299    });
     1300
     1301    // Character counter
     1302    $('#askeet-feedback-text').on('input', function() {
     1303        const count = $(this).val().length;
     1304        $('#askeet-char-count').text(count);
     1305        updateSubmitButton();
     1306    });
     1307
     1308    // Submit feedback
     1309    $('#askeet-submit-feedback').on('click', function() {
     1310        const btn = $(this);
     1311        const originalText = btn.text();
     1312        const feedbackText = $('#askeet-feedback-text').val().trim();
     1313
     1314        if (!feedbackSatisfaction) {
     1315            alert('Please select if you are satisfied or not.');
     1316            return;
     1317        }
     1318
     1319        btn.prop('disabled', true).text('Sending...');
     1320
     1321        // Get install_id from localized data
     1322        const installId = window.askeet_query_assistant && window.askeet_query_assistant.install_id ? window.askeet_query_assistant.install_id : '';
     1323        const apiUrl = window.askeet_query_assistant && window.askeet_query_assistant.api_url ? window.askeet_query_assistant.api_url : 'https://api.askeet.ai';
     1324
     1325        $.ajax({
     1326            url: apiUrl + '/submit-feedback',
     1327            type: 'POST',
     1328            contentType: 'application/json',
     1329            data: JSON.stringify({
     1330                install_id: installId,
     1331                satisfaction: feedbackSatisfaction,
     1332                feedback_text: feedbackText,
     1333                page_url: window.location.href,
     1334                user_agent: navigator.userAgent
     1335            }),
     1336            success: function(response) {
     1337                if (response.success) {
     1338                    // Show success message in modal
     1339                    showFeedbackSuccess();
     1340                } else {
     1341                    alert('Error: ' + (response.error || 'Failed to submit feedback'));
     1342                    btn.prop('disabled', false).text(originalText);
     1343                }
     1344            },
     1345            error: function() {
     1346                alert('Network error. Please try again.');
     1347                btn.prop('disabled', false).text(originalText);
     1348            }
     1349        });
     1350    });
     1351
     1352    function updateSubmitButton() {
     1353        const hasSelection = feedbackSatisfaction !== null;
     1354        $('#askeet-submit-feedback').prop('disabled', !hasSelection);
     1355    }
     1356
     1357    function closeFeedbackModal() {
     1358        $('#askeet-feedback-overlay').removeClass('active');
     1359    }
     1360
     1361    function resetFeedbackModal() {
     1362        feedbackSatisfaction = null;
     1363        $('.askeet-thumb-btn').removeClass('selected');
     1364        $('#askeet-feedback-text').val('');
     1365        $('#askeet-char-count').text('0');
     1366        $('#askeet-submit-feedback').prop('disabled', true).text(askeet_query_assistant_i18n.submit_feedback || 'Submit Feedback');
     1367
     1368        // Reset to normal view (hide success)
     1369        $('.askeet-feedback-body, .askeet-feedback-footer').show();
     1370        $('.askeet-feedback-success').remove();
     1371    }
     1372
     1373    function showFeedbackSuccess() {
     1374        // Hide body and footer
     1375        $('.askeet-feedback-body, .askeet-feedback-footer').hide();
     1376
     1377        // Show success message
     1378        const successHtml = `
     1379            <div class="askeet-feedback-success" style="padding: 60px 30px; text-align: center;">
     1380                <div style="width: 80px; height: 80px; background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); border-radius: 50%; margin: 0 auto 24px; display: flex; align-items: center; justify-content: center;">
     1381                    <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
     1382                        <polyline points="20 6 9 17 4 12"></polyline>
     1383                    </svg>
     1384                </div>
     1385                <h3 style="font-size: 24px; font-weight: 700; color: #1a1a1a; margin: 0 0 12px;">Thank You!</h3>
     1386                <p style="font-size: 16px; color: #666; margin: 0 0 24px;">Your feedback has been submitted successfully.</p>
     1387                <button type="button" class="askeet-submit-btn" onclick="jQuery('#askeet-feedback-overlay').removeClass('active');" style="background: linear-gradient(135deg, #4caf50 0%, #45a049 100%);">
     1388                    Close
     1389                </button>
     1390            </div>
     1391        `;
     1392
     1393        $('.askeet-feedback-modal').append(successHtml);
     1394
     1395        // Auto close after 3 seconds
     1396        setTimeout(function() {
     1397            closeFeedbackModal();
     1398            // Reset after close animation
     1399            setTimeout(resetFeedbackModal, 300);
     1400        }, 3000);
     1401    }
     1402
     1403    // Close modal on Escape key
     1404    $(document).on('keydown', function(e) {
     1405        if (e.key === 'Escape' && $('#askeet-feedback-overlay').hasClass('active')) {
     1406            closeFeedbackModal();
     1407        }
     1408    });
     1409
     1410    // ========================================
     1411    // STAR RATING SYSTEM
     1412    // ========================================
     1413
     1414    // Get install_id and api_url from localized data
     1415    const installId = window.askeet_query_assistant && window.askeet_query_assistant.install_id ? window.askeet_query_assistant.install_id : '';
     1416    const apiUrl = window.askeet_query_assistant && window.askeet_query_assistant.api_url ? window.askeet_query_assistant.api_url : 'https://api.askeet.ai';
     1417
     1418    // Star hover effect
     1419    $(document).on('mouseenter', '.wcqa-star', function() {
     1420        const container = $(this).closest('.wcqa-stars-container');
     1421        if (container.hasClass('rated')) return; // Don't change if already rated
     1422
     1423        const rating = $(this).data('rating');
     1424        container.find('.wcqa-star').each(function() {
     1425            const starRating = $(this).data('rating');
     1426            if (starRating <= rating) {
     1427                $(this).html('&#9733;').addClass('hovered'); // Filled star
     1428            } else {
     1429                $(this).html('&#9734;').removeClass('hovered'); // Empty star
     1430            }
     1431        });
     1432    });
     1433
     1434    // Star mouse leave - reset if not rated
     1435    $(document).on('mouseleave', '.wcqa-stars-container', function() {
     1436        if ($(this).hasClass('rated')) return;
     1437
     1438        $(this).find('.wcqa-star').each(function() {
     1439            $(this).html('&#9734;').removeClass('hovered');
     1440        });
     1441    });
     1442
     1443    // Star click - submit rating
     1444    $(document).on('click', '.wcqa-star', function() {
     1445        const container = $(this).closest('.wcqa-stars-container');
     1446        if (container.hasClass('rated')) return; // Already rated
     1447
     1448        const rating = $(this).data('rating');
     1449        const messageId = container.data('message-id');
     1450        const feedbackSpan = container.siblings('.wcqa-rating-feedback');
     1451        const messageContainer = container.closest('.wcqa-message');
     1452        const messageContent = messageContainer.find('.query-success, .query-error, .wcqa-table-wrapper').first().text().substring(0, 200);
     1453
     1454        // Show filled stars up to selected rating
     1455        container.find('.wcqa-star').each(function() {
     1456            const starRating = $(this).data('rating');
     1457            if (starRating <= rating) {
     1458                $(this).html('&#9733;').addClass('selected');
     1459            } else {
     1460                $(this).html('&#9734;').removeClass('selected');
     1461            }
     1462        });
     1463
     1464        // Mark as rated
     1465        container.addClass('rated');
     1466        feedbackSpan.text(askeet_query_assistant_i18n.rating_sending || 'Sending...');
     1467
     1468        // Submit rating to API
     1469        $.ajax({
     1470            url: apiUrl + '/submit-rating',
     1471            type: 'POST',
     1472            contentType: 'application/json',
     1473            data: JSON.stringify({
     1474                install_id: installId,
     1475                rating: rating,
     1476                message_id: messageId,
     1477                query: lastQueryRequest || '',
     1478                response_preview: messageContent
     1479            }),
     1480            success: function(response) {
     1481                if (response.success) {
     1482                    feedbackSpan.text(askeet_query_assistant_i18n.rating_thank_you || 'Thank you!').addClass('success');
     1483                } else {
     1484                    feedbackSpan.text(askeet_query_assistant_i18n.rating_error || 'Error').addClass('error');
     1485                    // Allow retry
     1486                    container.removeClass('rated');
     1487                }
     1488            },
     1489            error: function() {
     1490                feedbackSpan.text(askeet_query_assistant_i18n.rating_error || 'Error').addClass('error');
     1491                // Allow retry
     1492                container.removeClass('rated');
     1493            }
     1494        });
     1495    });
    11361496});
Note: See TracChangeset for help on using the changeset viewer.