PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.2.5
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.2.5
8.7.7 8.7.6 8.7.5 8.7.4 8.7.3 8.7.2 8.7.1 8.7.0 8.6.9 8.6.8 8.6.7 8.6.6 8.6.5 8.6.4 8.6.2 8.6.1 8.6.0 8.5.9 8.5.8 8.5.7 8.5.6 8.5.5 8.5.4 8.5.3 8.5.2 All 533 releases
chatbot / includes / admin / templates / rag.php

rag.php in WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services 8.2.5, at includes/admin/templates/rag.php

633 lines 28.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit; // Exit if accessed directly
3 /**
4 * AI Assistant Template - RAG Feature Extended
5 * @package Botmaster
6 */
7
8 $wpchatbot_license_valid = get_option('wpchatbot_license_valid');
9 // $wpchatbot_license_valid = 'starter';
10
11 ?>
12
13 <div class="qcl-openai">
14
15
16
17 <h2 class="nav-tab-wrapper">
18 <a href="#qcld-rag-settings-tab" class="nav-tab nav-tab-active"> Setting options</a>
19 <a href="#rag-sync" class="nav-tab">Sync and upload options</a>
20 <a href="#rag-database" class="nav-tab">KnowledgeBase Database</a>
21 </h2>
22
23 <div id="qcld-rag-settings-tab" class="qcld-tab-content active">
24 <!-- ===========================
25 EMBEDDING SOURCE OPTIONS
26 ============================ -->
27 <div class="wrap">
28 <h3>Choose Data Sources to Embed</h3>
29
30 <div class="mb-3">
31 <input type="checkbox" id="rag_embed_pages" <?php checked(get_option('rag_embed_pages'), '1'); ?>>
32 <label for="rag_embed_pages">Pages</label>
33 </div>
34
35 <div class="mb-3">
36 <input type="checkbox" id="rag_embed_posts" <?php checked(get_option('rag_embed_posts'), '1'); ?>>
37 <label for="rag_embed_posts">Posts</label>
38 </div>
39 <div class="mb-3">
40 <input type="checkbox" id="rag_embed_str" <?php checked(get_option('rag_embed_str'), '1'); ?>>
41 <label for="rag_embed_str">Simple Text Responses</label>
42 </div>
43 <div class="mb-3">
44 <?php
45 $custom_post_types = get_post_types(['public' => true, '_builtin' => false], 'objects');
46 $selected_cpts = get_option('rag_embed_cpts', []);
47 ?>
48
49 <label><strong>Custom Post Types:</strong></label><br>
50
51 <div class="rag_embed_cpts_wrapper">
52 <?php foreach ($custom_post_types as $cpt): ?>
53 <div class="rag_cpt_checkbox">
54 <input type="checkbox" class="rag_embed_cpts_checkbox"
55 id="rag_cpt_<?php echo esc_attr($cpt->name); ?>"
56 value="<?php echo esc_attr($cpt->name); ?>"
57 <?php echo in_array($cpt->name, $selected_cpts) ? 'checked' : ''; ?>>
58 <label for="rag_cpt_<?php echo esc_attr($cpt->name); ?>"><?php echo esc_html($cpt->label); ?></label>
59 </div>
60 <?php endforeach; ?>
61 </div>
62 <p class="description">Select multiple custom post types to embed.</p>
63 </div>
64
65
66 </div>
67 <!-- ===========================
68 EXECUTION BUTTON
69 ============================ -->
70 <div class="wrap my-4">
71 <p style="color: red"> <b><?php esc_html_e('Please connect to an AI service like OpenAI or Gemini before embedding. ', 'chatbot'); ?></b><b><a target="_blank" href="https://wpbot.pro/docs/knowledgebase/how-to-use-an-embedded-vector-database-and-rag-to-get-customized-responses-from-ai/"><?php esc_html_e('Check this Tutorial for more details.', 'chatbot'); ?></a></b></p>
72 <form method="post" id="rag_embed_form">
73 <input type="hidden" name="embed_all_sources" value="1">
74 <button type="button" id="rag_embed_btn" class="button button-primary">Embed All Selected Sources</button>
75 </form>
76
77 <?php
78 if (isset($_POST['embed_all_sources'])):
79 if( ( get_option( 'ai_enabled') == 1 && get_option('open_ai_api_key') ) || ( get_option('qcld_gemini_enabled') == 1 && get_option('qcld_gemini_api_key') ) || ( get_option('qcld_openrouter_enabled') == 1 && get_option('qcld_openrouter_api_key') ) ){
80 ?>
81 <h3>Embedding started...</h3>
82 <?php Qcld_Bot_Rag::instance()->wp_rag_embed_all_sources(); ?>
83 <?php }else{ ?>
84 <Script>
85 swal.fire('', 'Please connect to an AI service like OpenAI or Gemini with API key before embedding.', 'warning');
86 </Script>
87 <?php
88 }
89 endif;
90 ?>
91 </div>
92 <!-- ===========================
93 SAVE SETTINGS BUTTON
94 ============================ -->
95 <div class="wrap">
96 <button class="qcld-btn-primary" id="save_rag_setting">Save Settings</button>
97 </div>
98 </div>
99
100
101 <div id="rag-sync" class="qcld-tab-content">
102 <?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'): ?>
103 <div class="wrap">
104 <div style="background-color: #fee; border: 1px solid #c33; padding: 15px; margin: 20px 0; border-radius: 4px;">
105 <p style="color: #c33; font-weight: bold; margin: 0; font-size: 14px;">
106 These options are available with the WPBot Pro <a href="https://www.wpbot.pro/pricing/" target="_blank" style="color: #c33; text-decoration: underline;">Professional</a> and <a href="https://www.wpbot.pro/pricing/" target="_blank" style="color: #c33; text-decoration: underline;">Master</a> Licenses
107 </p>
108 </div>
109 </div>
110 <?php endif; ?>
111
112 <div style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
113 <!-- ===========================
114 SYNC SETTINGS
115 ============================ -->
116 <div class="wrap">
117 <h3>Sync Settings</h3>
118
119 <div class="mb-3">
120 <input type="checkbox" id="rag_auto_sync_enabled" <?php checked(get_option('rag_auto_sync_enabled'), '1'); ?>>
121 <label for="rag_auto_sync_enabled"><strong>Enable Auto Sync (on Save)</strong></label>
122 <p class="description">Automatically update embeddings when a post or product is saved/updated.</p>
123 </div>
124
125 </div>
126
127
128 <!-- ===========================
129 PDF UPLOAD (AJAX)
130 ============================ -->
131 <div class="wrap">
132 <h3>Upload PDF for RAG</h3>
133
134 <form id="rag-pdf-form">
135 <input type="file" id="rag-pdf-files" name="rag_pdf[]" multiple accept="application/pdf" />
136 <br>
137 <button type="submit" class="button button-primary" id="rag-pdf-submit">Upload & Embed PDF</button>
138 <span id="rag-pdf-status" style="margin-left: 10px;"></span>
139 </form>
140
141 <div id="rag-pdf-output" style="margin-top: 15px;"></div>
142 </div>
143
144 <!-- ===========================
145 CSV UPLOAD (AJAX)
146 ============================ -->
147 <div class="wrap">
148 <h3>Upload CSV Data for RAG</h3>
149 <p>Upload CSV files with data to be embedded. Each row will be processed as a separate document. <a href="<?php echo plugin_dir_url(__FILE__).'download/rag_test_data.csv'; ?>">Download Test Data</a></p>
150
151 <form id="rag-csv-form">
152 <input type="file" id="rag-csv-files" name="rag_csv[]" multiple accept=".csv,text/csv" />
153 <br>
154 <button type="submit" class="button button-primary" id="rag-csv-submit">Upload & Embed CSV</button>
155 <span id="rag-csv-status" style="margin-left: 10px;"></span>
156 </form>
157
158 <div id="rag-csv-output" style="margin-top: 15px;"></div>
159 </div>
160
161 <!-- ===========================
162 XAML UPLOAD (AJAX)
163 ============================ -->
164 <div class="wrap">
165 <h3>Upload XML Data for RAG</h3>
166 <p>Upload XML files with data to be embedded.</p>
167
168 <form id="rag-xaml-form">
169 <input type="file" id="rag-xaml-files" name="rag_xaml[]" multiple accept=".xaml,text/xml,application/xml" />
170 <br>
171 <button type="submit" class="button button-primary" id="rag-xaml-submit">Upload & Embed XML</button>
172 <span id="rag-xaml-status" style="margin-left: 10px;"></span>
173 </form>
174
175 <div id="rag-xaml-output" style="margin-top: 15px;"></div>
176 </div>
177
178 <!-- ===========================
179 SITEMAP SUBMISSION
180 ============================ -->
181 <div class="wrap">
182 <h3>Submit Sitemap for RAG</h3>
183 <p>Enter your XML Sitemap URL to crawl and embed all pages.</p>
184 <input type="url" id="botmaster_sitemap_url" class="regular-text" placeholder="https://example.com/sitemap.xml" style="width: 100%; max-width: 400px;">
185 <button type="button" id="botmaster_submit_sitemap_btn" class="button button-primary">Process Sitemap</button>
186 <div id="botmaster_sitemap_status" style="margin-top: 10px;"></div>
187 </div>
188 </div>
189
190 <div class="wrap">
191 <button class="qcld-btn-primary" id="save_rag_setting">Save Settings</button>
192 </div>
193 </div>
194
195 <style>
196 .qcld-tab-content {
197 display: none !important;
198 }
199
200 .qcld-tab-content.active {
201 display: block !important;
202 }
203 </style>
204
205 <script>
206 jQuery(document).ready(function($) {
207 $('#botmaster_submit_sitemap_btn').on('click', function() {
208 var sitemapUrl = $('#botmaster_sitemap_url').val();
209 var statusDiv = $('#botmaster_sitemap_status');
210
211 if (!sitemapUrl) {
212 alert('Please enter a Sitemap URL');
213 return;
214 }
215
216 statusDiv.html('Processing... please wait.');
217 $(this).prop('disabled', true);
218
219 $.post(ajaxurl, {
220 action: 'botmaster_submit_sitemap',
221 sitemap_url: sitemapUrl,
222 nonce: '<?php echo wp_create_nonce("botmaster_kb_nonce"); ?>'
223 }, function(response) {
224 $('#botmaster_submit_sitemap_btn').prop('disabled', false);
225 if (response.success) {
226 statusDiv.html('<div class="notice notice-success inline"><p>' + response.data.message + '</p></div>');
227 } else {
228 statusDiv.html('<div class="notice notice-error inline"><p>Error: ' + response.data + '</p></div>');
229 }
230 });
231 });
232 });
233 </script>
234
235
236
237
238
239 <div id="rag-database" class="qcld-tab-content">
240 <!-- ===========================
241 KNOWLEDGE BASE MANAGEMENT
242 ============================ -->
243 <div class="wrap">
244 <h3>Knowledge Base</h3>
245
246 <div class="tablenav top">
247 <div class="alignleft actions bulkactions">
248 <select id="rag-bulk-action-selector">
249 <option value="-1">Bulk Actions</option>
250 <option value="delete">Delete</option>
251 </select>
252 <button type="button" id="rag-apply-bulk-action" class="button action">Apply</button>
253 </div>
254 <div class="alignleft actions">
255 <button type="button" id="rag-delete-all" class="button button-link-delete" style="margin-left: 10px;">Delete All</button>
256
257
258 <div class="alignright actions" style="margin-left: 10px;">
259 <form method="get" style="display:inline-block;" action="?page=wpbot_openAi#ai-knowledge-base-tab#rag-database">
260 <input type="hidden" name="page" value="<?php echo esc_attr( sanitize_text_field( wp_unslash( $_GET['page'] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>">
261 <?php if (isset($_GET['post_type'])): // phpcs:ignore WordPress.Security.NonceVerification ?>
262 <input type="hidden" name="post_type" value="<?php echo esc_attr( sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>">
263 <?php endif; ?>
264 <p class="search-box" style="margin:0;">
265 <input type="search" id="rag-search-input" name="s" value="<?php echo esc_attr( sanitize_text_field( wp_unslash( $_GET['s'] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>" placeholder="Search documents...">
266 <input type="submit" id="search-submit" class="button" value="Search">
267 </p>
268 </form>
269 </div>
270 </div>
271
272 <br class="clear">
273 </div>
274 <?php
275 global $wpdb;
276 $table_rag_documents = $wpdb->prefix . 'rag_documents';
277
278 // Handle Table Creation
279 if (isset($_POST['rag_create_table']) && check_admin_referer('rag_create_table_nonce')) {
280 $charset = $wpdb->get_charset_collate();
281 $sql_rag_documents = "CREATE TABLE $table_rag_documents (
282 id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
283 doc_id VARCHAR(100) DEFAULT NULL,
284 title VARCHAR(255) NOT NULL,
285 content LONGTEXT NOT NULL,
286 embedding LONGTEXT NOT NULL,
287 source_type VARCHAR(20) DEFAULT 'post',
288 source_url VARCHAR(255) DEFAULT NULL,
289 file_url TEXT DEFAULT NULL,
290 metadata LONGTEXT DEFAULT NULL,
291 status VARCHAR(50) DEFAULT 'complete',
292 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
293 ) $charset;";
294 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
295 dbDelta($sql_rag_documents);
296 echo '<div class="notice notice-success inline"><p>Database table created successfully.</p></div>';
297 }
298
299 // Check if table exists
300 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $wpdb->esc_like($table_rag_documents))) === $table_rag_documents;
301
302 if (!$table_exists) {
303 ?>
304 <div class="wrap">
305 <h3>Knowledge Base Database</h3>
306 <div class="notice notice-warning inline">
307 <p>The <strong><?php echo esc_html($table_rag_documents); ?></strong> table does not exist. Please create it to start using the Knowledge Base.</p>
308 </div>
309 <form method="post">
310 <?php wp_nonce_field('rag_create_table_nonce'); ?>
311 <input type="hidden" name="rag_create_table" value="1">
312 <button type="submit" class="button button-primary">Create Database Table</button>
313 </form>
314 </div>
315 <?php
316 } else {
317 ?>
318 <?php
319 $table_name = $table_rag_documents;
320
321 $search_query = isset($_GET['s']) ? sanitize_text_field(wp_unslash($_GET['s'])) : '';
322
323 if ($search_query) {
324 $like = '%' . $wpdb->esc_like($search_query) . '%';
325 $total_items = $wpdb->get_var($wpdb->prepare("SELECT COUNT(id) FROM {$table_name} WHERE title LIKE %s OR content LIKE %s", $like, $like));
326 } else {
327 $total_items = $wpdb->get_var("SELECT COUNT(id) FROM {$table_name}");
328 }
329 $items_per_page = 50;
330 $page = isset($_GET['paged']) ? absint(wp_unslash($_GET['paged'])) : 1;
331 $offset = ($page - 1) * $items_per_page;
332 $total_pages = ceil($total_items / $items_per_page);
333
334 $pagination_args = array(
335 'base' => add_query_arg(array('paged' => '%#%', 's' => $search_query), '?page=wpbot_openAi') . '#ai-knowledge-base-tab#rag-database',
336 'format' => '',
337 'prev_text' => __('&laquo;'),
338 'next_text' => __('&raquo;'),
339 'total' => $total_pages,
340 'current' => $page,
341 'type' => 'plain',
342 );
343
344 if ($total_pages > 1) {
345 echo '<div class="tablenav-pages"><span class="displaying-num">' . sprintf(_n('%s item', '%s items', $total_items), number_format_i18n($total_items)) . '</span>';
346 echo paginate_links($pagination_args);
347 echo '</div>';
348 }
349 ?>
350
351 <table class="wp-list-table widefat fixed striped">
352 <thead>
353 <tr>
354 <td id="cb" class="manage-column column-cb check-column">
355 <input type="checkbox" id="rag-select-all">
356 </td>
357 <th>Title</th>
358 <th>Content</th>
359 <th>Source Type</th>
360 <th>URL/File</th>
361 <th>Status</th>
362 <th>Actions</th>
363 </tr>
364 </thead>
365 <tbody id="rag-knowledge-base-list">
366 <?php
367 if ($search_query) {
368 $like = '%' . $wpdb->esc_like($search_query) . '%';
369 $documents = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table_name} WHERE title LIKE %s OR content LIKE %s ORDER BY created_at DESC LIMIT %d OFFSET %d", $like, $like, $items_per_page, $offset));
370 } else {
371 $documents = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table_name} ORDER BY created_at DESC LIMIT %d OFFSET %d", $items_per_page, $offset));
372 }
373
374 if ($documents) {
375 foreach ($documents as $doc) {
376 ?>
377 <tr id="rag-doc-<?php echo $doc->id; ?>">
378 <th scope="qcld-row" class="check-column">
379 <input type="checkbox" class="rag-doc-checkbox" value="<?php echo $doc->id; ?>">
380 </th>
381 <td><?php echo esc_html($doc->title); ?></td>
382 <td>
383 <div style="max-height: 4.5em; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; line-height: 1.5em; font-size: 13px; color: #666;">
384 <?php echo wp_kses_post($doc->content); ?>
385 </div>
386 </td>
387 <td><?php echo esc_html($doc->source_type); ?></td>
388 <td><?php echo esc_html($doc->source_url ?: $doc->file_url); ?></td>
389 <td><?php echo esc_html($doc->status); ?></td>
390 <td>
391 <?php if (!in_array($doc->source_type, ['csv', 'xml', 'xaml','sitemap'])): ?>
392 <button class="button button-small rag-sync-doc" data-id="<?php echo $doc->id; ?>" title="Re-sync data from source">Sync</button>
393 <?php endif; ?>
394 <button class="button button-small rag-edit-doc" data-id="<?php echo $doc->id; ?>">Edit</button>
395 <button class="button button-small button-link-delete rag-delete-doc" data-id="<?php echo $doc->id; ?>">Delete</button>
396 </td>
397 </tr>
398 <?php
399 }
400 } else {
401 echo '<tr><td colspan="7">No documents found in knowledge base.</td></tr>';
402 }
403 ?>
404 </tbody>
405 </table>
406
407 <div class="tablenav bottom">
408 <?php
409 if ($total_pages > 1) {
410 echo '<div class="tablenav-pages"><span class="displaying-num">' . sprintf(_n('%s item', '%s items', $total_items), number_format_i18n($total_items)) . '</span>';
411 echo paginate_links($pagination_args);
412 echo '</div>';
413 }
414 ?>
415 <br class="clear">
416 </div>
417 <?php } ?>
418 </div>
419 </div>
420
421 <!-- Edit Document Modal -->
422 <div id="rag-edit-modal" style="display:none; position:fixed; z-index:99999; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.5);">
423 <div style="background:#fff; margin:10% auto; padding:20px; width:60%; border-radius:5px;">
424 <h4>Edit Knowledge Base Document</h4>
425 <input type="hidden" id="edit-doc-id">
426 <div class="mb-3">
427 <label>Title</label><br>
428 <input type="text" id="edit-doc-title" class="regular-text" style="width:100%;">
429 </div>
430 <div class="mb-3">
431 <label>Content</label><br>
432 <textarea id="edit-doc-content" rows="10" style="width:100%;"></textarea>
433 </div>
434 <p class="description">Note: Updating content will re-generate embeddings.</p>
435 <div class="mt-3">
436 <button id="save-edit-doc" class="button button-primary">Save Changes</button>
437 <button id="close-edit-modal" class="button">Cancel</button>
438 </div>
439 </div>
440 </div>
441 </div>
442
443
444
445 <script>
446 jQuery(document).ready(function($) {
447 // PDF Upload Handler
448 $('#rag-pdf-form').on('submit', function(e) {
449 e.preventDefault();
450
451 var fileInput = $('#rag-pdf-files')[0];
452 if (!fileInput.files.length) {
453 alert('Please select PDF files to upload');
454 return;
455 }
456
457 var formData = new FormData();
458 for (var i = 0; i < fileInput.files.length; i++) {
459 formData.append('rag_pdf[]', fileInput.files[i]);
460 }
461 formData.append('action', 'rag_upload_pdf');
462 formData.append('nonce', '<?php echo wp_create_nonce('rag_upload_nonce'); ?>');
463
464 $('#rag-pdf-submit').prop('disabled', true);
465 $('#rag-pdf-status').html('<span class="spinner is-active" style="float:none;"></span> Uploading and processing...');
466 $('#rag-pdf-output').html('');
467
468 $.ajax({
469 url: ajaxurl,
470 type: 'POST',
471 data: formData,
472 processData: false,
473 contentType: false,
474 success: function(response) {
475 $('#rag-pdf-submit').prop('disabled', false);
476 if (response.success) {
477 $('#rag-pdf-status').html('<span style="color:green;">✓ Complete</span>');
478 $('#rag-pdf-output').html(response.data.output);
479 $('#rag-pdf-files').val('');
480 } else {
481 $('#rag-pdf-status').html('<span style="color:red;">✗ Error</span>');
482 $('#rag-pdf-output').html('<p style="color:red;">' + response.data.message + '</p>');
483 }
484 },
485 error: function(xhr) {
486 $('#rag-pdf-submit').prop('disabled', false);
487 $('#rag-pdf-status').html('<span style="color:red;">✗ Error</span>');
488 $('#rag-pdf-output').html('<p style="color:red;">Upload failed. Please try again.</p>');
489 }
490 });
491 });
492
493 // CSV Upload Handler
494 $('#rag-csv-form').on('submit', function(e) {
495 e.preventDefault();
496
497 var fileInput = $('#rag-csv-files')[0];
498 if (!fileInput.files.length) {
499 alert('Please select CSV files to upload');
500 return;
501 }
502
503 var formData = new FormData();
504 for (var i = 0; i < fileInput.files.length; i++) {
505 formData.append('rag_csv[]', fileInput.files[i]);
506 }
507 formData.append('action', 'rag_upload_csv');
508 formData.append('nonce', '<?php echo wp_create_nonce('rag_upload_nonce'); ?>');
509
510 $('#rag-csv-submit').prop('disabled', true);
511 $('#rag-csv-status').html('<span class="spinner is-active" style="float:none;"></span> Uploading and processing...');
512 $('#rag-csv-output').html('');
513
514 $.ajax({
515 url: ajaxurl,
516 type: 'POST',
517 data: formData,
518 processData: false,
519 contentType: false,
520 success: function(response) {
521 $('#rag-csv-submit').prop('disabled', false);
522 if (response.success) {
523 $('#rag-csv-status').html('<span style="color:green;">✓ Complete</span>');
524 $('#rag-csv-output').html(response.data.output);
525 $('#rag-csv-files').val('');
526 } else {
527 $('#rag-csv-status').html('<span style="color:red;">✗ Error</span>');
528 $('#rag-csv-output').html('<p style="color:red;">' + response.data.message + '</p>');
529 }
530 },
531 error: function(xhr) {
532 $('#rag-csv-submit').prop('disabled', false);
533 $('#rag-csv-status').html('<span style="color:red;">✗ Error</span>');
534 $('#rag-csv-output').html('<p style="color:red;">Upload failed. Please try again.</p>');
535 }
536 });
537 });
538
539 // XAML Upload Handler
540 $('#rag-xaml-form').on('submit', function(e) {
541 e.preventDefault();
542
543 var fileInput = $('#rag-xaml-files')[0];
544 if (!fileInput.files.length) {
545 alert('Please select XAML files to upload');
546 return;
547 }
548
549 var formData = new FormData();
550 for (var i = 0; i < fileInput.files.length; i++) {
551 formData.append('rag_xaml[]', fileInput.files[i]);
552 }
553 formData.append('action', 'rag_upload_xaml');
554 formData.append('nonce', '<?php echo wp_create_nonce('rag_upload_nonce'); ?>');
555
556 $('#rag-xaml-submit').prop('disabled', true);
557 $('#rag-xaml-status').html('<span class="spinner is-active" style="float:none;"></span> Uploading and processing...');
558 $('#rag-xaml-output').html('');
559
560 $.ajax({
561 url: ajaxurl,
562 type: 'POST',
563 data: formData,
564 processData: false,
565 contentType: false,
566 success: function(response) {
567 $('#rag-xaml-submit').prop('disabled', false);
568 if (response.success) {
569 $('#rag-xaml-status').html('<span style="color:green;">✓ Complete</span>');
570 $('#rag-xaml-output').html(response.data.output);
571 $('#rag-xaml-files').val('');
572 } else {
573 $('#rag-xaml-status').html('<span style="color:red;">✗ Error</span>');
574 $('#rag-xaml-output').html('<p style="color:red;">' + response.data.message + '</p>');
575 }
576 },
577 error: function(xhr) {
578 $('#rag-xaml-submit').prop('disabled', false);
579 $('#rag-xaml-status').html('<span style="color:red;">✗ Error</span>');
580 $('#rag-xaml-output').html('<p style="color:red;">Upload failed. Please try again.</p>');
581 }
582 });
583 });
584 });
585 </script>
586 <script>
587 jQuery(document).ready(function($) {
588 // Tab switching logic
589 $('.nav-tab-wrapper').on('click', '.nav-tab', function(e) {
590 var $tab = $(this);
591 var targetId = $tab.attr('href');
592 var $content = $(targetId);
593
594 if ($tab.data('disabled')) {
595 e.preventDefault();
596 return false;
597 }
598
599 if ($content.length) {
600 e.preventDefault();
601 // Update tabs
602 $('.nav-tab').removeClass('nav-tab-active');
603 $tab.addClass('nav-tab-active');
604
605 // Update content area
606 $('.qcld-tab-content').removeClass('active');
607 $content.addClass('active');
608
609 // Update URL hash without jumping
610 var compositeHash = '#ai-knowledge-base-tab' + targetId;
611 if (history.pushState) {
612 history.pushState(null, null, compositeHash);
613 } else {
614 window.location.hash = compositeHash;
615 }
616 }
617 });
618
619 // Handle hash on page load
620 var hash = window.location.hash;
621 if (hash && hash.includes('#rag-')) {
622 var subTabHash = '#' + hash.split('#').pop();
623 var $targetTab = $('.nav-tab-wrapper a[href="' + subTabHash + '"]');
624 if ($targetTab.length) {
625 $targetTab.trigger('click');
626 }
627 } else if (hash === '#ai-knowledge-base-tab') {
628 // Default to first tab if no sub-tab specified
629 $('.nav-tab-wrapper a').first().trigger('click');
630 }
631 });
632 </script>
633