PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.7.5
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.7.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.7.5, at includes/admin/templates/rag.php

999 lines 59.1 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 if (get_option('wpchatbot_license_valid') == 'master' && get_option('wpchatbot_license_valid') == 'professional') {
8 update_option('wpchatbot_license_valid', 'starter');
9 }
10
11 $wpchatbot_license_valid = get_option('wpchatbot_license_valid');
12 // $wpchatbot_license_valid = 'starter';
13
14 ?>
15
16 <div class="qcl-openai">
17
18
19
20 <h2 class="nav-tab-wrapper">
21 <a href="#qcld-rag-settings-tab" class="nav-tab nav-tab-active"> Setting options</a>
22 <a href="#qcld-rag-settings-sql" class="nav-tab">RAG from DB Tables</a>
23 <a href="#rag-sync" class="nav-tab">Sync and upload options</a>
24 <a href="#rag-sources" class="nav-tab">Manage Sources</a>
25 <a href="#rag-database" class="nav-tab">KnowledgeBase Database</a>
26 </h2>
27
28 <div id="qcld-rag-settings-tab" class="qcld-tab-content active">
29 <!-- ===========================
30 EMBEDDING SOURCE OPTIONS
31 ============================ -->
32 <div class="wrap">
33 <h3>Choose Data Sources to Embed</h3>
34
35 <div class="mb-3">
36 <input type="checkbox" id="rag_embed_pages" <?php checked(get_option('rag_embed_pages'), '1'); ?>>
37 <label for="rag_embed_pages">Pages</label>
38 </div>
39
40 <div class="mb-3">
41 <input type="checkbox" id="rag_embed_posts" <?php checked(get_option('rag_embed_posts'), '1'); ?>>
42 <label for="rag_embed_posts">Posts</label>
43 </div>
44 <div class="mb-3">
45 <input type="checkbox" id="rag_embed_str" <?php checked(get_option('rag_embed_str'), '1'); ?>>
46 <label for="rag_embed_str">Simple Text Responses</label>
47 </div>
48 <div class="mb-3">
49 <?php
50 $custom_post_types = get_post_types(['public' => true, '_builtin' => false], 'objects');
51 $selected_cpts = get_option('rag_embed_cpts', []);
52 ?>
53
54 <label><strong>Custom Post Types:</strong></label><br>
55
56 <div class="rag_embed_cpts_wrapper">
57 <?php foreach ($custom_post_types as $cpt): ?>
58 <div class="rag_cpt_checkbox">
59 <input type="checkbox" class="rag_embed_cpts_checkbox"
60 id="rag_cpt_<?php echo esc_attr($cpt->name); ?>"
61 value="<?php echo esc_attr($cpt->name); ?>"
62 <?php echo in_array($cpt->name, $selected_cpts) ? 'checked' : ''; ?>>
63 <label for="rag_cpt_<?php echo esc_attr($cpt->name); ?>"><?php echo esc_html($cpt->label); ?></label>
64 </div>
65 <?php endforeach; ?>
66 </div>
67 <p class="description">Select multiple custom post types to embed.</p>
68 </div>
69 <div class="mb-4" style="background: #f9f9f9; padding: 15px; border-radius: 8px; border-left: 4px solid #007bff;">
70 <h4 style="margin-top: 0;"><i class="fa fa-tags"></i> Post Meta Data Settings <span class="pro-badge" style="display:inline-block; margin-left:8px; padding:2px 6px; background:#f0b429; color:#fff; border-radius:3px; font-size:11px; font-weight:bold; text-transform:uppercase;">PRO</span></h4>
71 <div class="mb-3">
72 <input type="checkbox" id="rag_embed_meta" <?php checked(get_option('rag_embed_meta'), '1'); ?> disabled>
73 <label for="rag_embed_meta"><strong>Include Post Meta Data</strong></label>
74 <p class="description">Enable this to include custom fields/meta data in the embeddings.</p>
75 </div>
76
77 <div class="mb-3">
78 <label for="rag_embed_meta_keys"><strong>Meta Keys to Include (comma separated):</strong></label><br>
79 <input type="text" id="rag_embed_meta_keys" value="<?php echo esc_attr(get_option('rag_embed_meta_keys', '')); ?>" placeholder="e.g. _price, _sku, custom_field_key" style="width: 100%; max-width: 400px; margin-top: 5px;" disabled>
80 <p class="description">Specify the meta keys you want to include in the context. Leave empty to include all meta (not recommended).</p>
81 </div>
82 </div>
83
84 </div>
85 <div class="wrap">
86 <h3>Sync Settings</h3>
87
88 <div class="mb-3">
89 <input type="checkbox" id="rag_auto_sync_enabled" class="rag_auto_sync_enabled" <?php checked(get_option('rag_auto_sync_enabled'), '1'); ?>>
90 <label for="rag_auto_sync_enabled"><strong>Enable Auto Sync (on Save)</strong></label>
91 <p class="description">Automatically update embeddings when a post or product is saved/updated.</p>
92 </div>
93 </div>
94 <!-- ===========================
95 EXECUTION BUTTON
96 ============================ -->
97 <div class="wrap my-4">
98 <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>
99 <form method="post" id="rag_embed_form">
100 <input type="hidden" name="embed_all_sources" value="1">
101 <button type="button" id="rag_embed_btn" class="button button-primary">Embed All Selected Sources</button>
102 </form>
103
104 <?php
105 if (isset($_POST['embed_all_sources'])):
106 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') ) ){
107 ?>
108 <h3>Embedding started...</h3>
109 <?php Qcld_Bot_Rag::instance()->wp_rag_embed_all_sources(); ?>
110 <?php }else{ ?>
111 <Script>
112 swal.fire('', 'Please connect to an AI service like OpenAI or Gemini with API key before embedding.', 'warning');
113 </Script>
114 <?php
115 }
116 endif;
117 ?>
118 </div>
119 <!-- ===========================
120 SAVE SETTINGS BUTTON
121 ============================ -->
122 <div class="wrap">
123 <button class="qcld-btn-primary" id="save_rag_setting">Save Settings</button>
124 </div>
125 </div>
126 <div id="qcld-rag-settings-sql" class="qcld-tab-content">
127
128 <div class="wrap">
129 <h3>Choose Data Sources to Embed</h3>
130 <?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'): ?>
131 <div class="wrap">
132 <div style="background-color: #fee; border: 1px solid #c33; padding: 15px; margin: 20px 0; border-radius: 4px;">
133 <p style="color: #c33; font-weight: bold; margin: 0; font-size: 14px;">
134 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
135 </p>
136 </div>
137 </div>
138 <?php endif; ?>
139 <div class="mb-3" style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
140 <input type="checkbox" id="rag_embed_sql" checked disabled <?php checked(get_option('rag_embed_sql'), '1'); ?>>
141 <label for="rag_embed_sql">Database Table (SQL)</label>
142 <div id="rag_sql_options" style="">
143 <table id="rag_sql_tables_list" class="wp-list-table widefat striped" style="max-width: 600px; margin-bottom: 10px;">
144 <thead>
145 <tr>
146 <th>Table Name</th>
147 <th>Table Field(s)</th>
148 <th>Actions</th>
149 </tr>
150 </thead>
151 <tbody>
152 <?php
153 $sql_tables = get_option('rag_sql_tables', []);
154 if (!is_array($sql_tables) && !empty($sql_tables)) {
155 $sql_tables = json_decode($sql_tables, true);
156 }
157
158 if (empty($sql_tables)) {
159 $table_name = get_option('rag_sql_table_name', '');
160 $table_field = get_option('rag_sql_table_field', '');
161 if ($table_name && $table_field) {
162 $sql_tables[] = ['table_name' => $table_name, 'table_field' => $table_field];
163 }
164 }
165
166 if (!empty($sql_tables)) {
167 foreach ($sql_tables as $table) {
168 ?>
169 <tr>
170 <td><input type="text" name="rag_sql_table_name[]" value="<?php echo esc_attr($table['table_name']); ?>" class="regular-text" style="width:100%;"></td>
171 <td><input type="text" name="rag_sql_table_field[]" value="<?php echo esc_attr($table['table_field']); ?>" class="regular-text" style="width:100%;"></td>
172 <td><button type="button" class="button button-link-delete rag_sql_remove_table">Remove</button></td>
173 </tr>
174 <?php
175 }
176 } else {
177 ?>
178 <tr>
179 <td><input type="text" name="rag_sql_table_name[]" value="" placeholder="e.g. wp_users" class="regular-text" style="width:100%;"></td>
180 <td><input type="text" name="rag_sql_table_field[]" value="" placeholder="e.g. user_email" class="regular-text" style="width:100%;"></td>
181 <td><button type="button" class="button button-link-delete rag_sql_remove_table">Remove</button></td>
182 </tr>
183 <?php
184 }
185 ?>
186 </tbody>
187 </table>
188 <button type="button" class="button button-primary" id="rag_sql_add_table">Add New Table</button>
189 <button type="button" class="button button-secondary" id="sql_instruction" style="margin-left: 5px;">View Available SQL Tables & Fields</button>
190 <p class="description">Add multiple tables and fields. Separate multiple fields in the same table with commas. Click "Create Missing Tables" to generate empty tables in the database if they don't exist.</p>
191 </div>
192 <!-- New Section for Google Sheets to SQL Sync -->
193 <div id="rag_sql_sheets_section" style="display:block; margin-top: 20px; margin-left: 20px; padding: 15px; border: 1px solid #ccc; background: #f9f9f9;">
194 <h4>Google Sheets & CSV Database Sync</h4>
195 <p class="description">Directly import Google Sheets or upload CSV data into SQL tables for Text-to-SQL querying.</p>
196
197 <div style="margin-bottom: 15px;">
198 <label style="display:block; font-weight:bold; margin-bottom:5px;">Import via Google Sheet URL:</label>
199 <input type="text" id="rag_sql_sheet_url_input" placeholder="Enter Google Sheet or CSV URL" class="regular-text" style="width: 60%; margin-right: 5px;">
200 <button type="button" class="button button-primary" id="rag_sql_import_sheet">Import URL to SQL</button>
201 </div>
202
203 <div style="margin-bottom: 15px;">
204 <label style="display:block; font-weight:bold; margin-bottom:5px;">Upload a Local CSV File:</label>
205 <input type="file" id="rag_sql_csv_file_input" accept=".csv" style="margin-right: 5px;">
206 <button type="button" class="button button-primary" id="rag_sql_upload_csv">Upload CSV to SQL</button>
207 </div>
208
209 <hr style="margin: 15px 0;">
210 <p>Sync all your previously uploaded sheets into SQL tables:</p>
211 <button type="button" class="button button-secondary" id="rag_sql_sync_existing_sheets">Sync Existing Sheets to SQL</button>
212
213 <div id="rag_sql_sync_status" style="margin-top: 10px; font-weight: bold;"></div>
214 </div>
215 </div>
216 <br><br>
217 <div class="mb-3" style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?> ">
218 <input type="checkbox" id="rag_sql_auto_sync_enabled" <?php checked(get_option('rag_sql_auto_sync_enabled'), '1'); ?>>
219 <label for="rag_sql_auto_sync_enabled"><strong>Enable Auto Sync (on Save)</strong></label>
220 <p class="description">Automatically update embeddings </p>
221 </div>
222 <hr>
223 <?php
224 $sql_sync_interval = get_option('rag_sql_sync_interval', 'daily');
225 $sql_interval_labels = [
226 'hourly' => '(Hourly)',
227 'twicedaily' => '(Twice Daily)',
228 'daily' => '(Daily)',
229 'weekly' => '(Weekly)',
230 ];
231 $sql_interval_desc = [
232 'hourly' => 'once per hour',
233 'twicedaily' => 'twice per day',
234 'daily' => 'once per day',
235 'weekly' => 'once per week',
236 ];
237 $sql_current_label = $sql_interval_labels[$sql_sync_interval] ?? '(Daily)';
238 $sql_current_desc = $sql_interval_desc[$sql_sync_interval] ?? 'once per day';
239 ?>
240
241 <div class="mb-3" style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
242 <label for="rag_sql_sync_interval"><strong>Sync Interval:</strong></label>
243 <select id="rag_sql_sync_interval">
244 <option value="five_minutes" <?php selected(get_option('rag_sql_sync_interval'), 'five_minutes'); ?>>Every 5 Minutes</option>
245 <option value="hourly" <?php selected(get_option('rag_sql_sync_interval'), 'hourly'); ?>>Hourly</option>
246 <option value="twicedaily" <?php selected(get_option('rag_sql_sync_interval'), 'twicedaily'); ?>>Twice Daily</option>
247 <option value="daily" <?php selected(get_option('rag_sql_sync_interval', 'daily'), 'daily'); ?>>Once Daily</option>
248 <option value="weekly" <?php selected(get_option('rag_sql_sync_interval'), 'weekly'); ?>>Weekly</option>
249 </select>
250 <p class="description">Choose how often to run the automated synchronization.</p>
251 </div>
252
253 </div>
254
255 <div class="wrap my-4" style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
256 <button type="button" id="rag_embed_sql_btn" class="button button-primary">Embed Selected Database Data</button>
257
258 <div id="rag_sql_progress_container" style="display:none; margin-top: 20px;">
259 <div class="progress" style="height: 25px; background-color: #f1f1f1; border-radius: 5px; overflow: hidden;">
260 <div id="rag_sql_progress_bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%; height: 100%; background-color: #007bff; transition: width 0.3s ease;"></div>
261 </div>
262 <p id="rag_sql_progress_status" style="margin-top: 10px; font-weight: bold;"></p>
263 </div>
264 </div>
265
266 <!-- SAVE SETTINGS BUTTON -->
267 <div class="wrap" style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
268 <button class="qcld-btn-primary" id="save_rag_setting">Save Settings</button>
269 </div>
270 </div>
271
272 <div id="rag-sync" class="qcld-tab-content">
273 <?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'): ?>
274 <div class="wrap">
275 <div style="background-color: #fee; border: 1px solid #c33; padding: 15px; margin: 20px 0; border-radius: 4px;">
276 <p style="color: #c33; font-weight: bold; margin: 0; font-size: 14px;">
277 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
278 </p>
279 </div>
280 </div>
281 <?php endif; ?>
282
283 <div style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
284 <!-- ===========================
285 SYNC SETTINGS
286 ============================ -->
287 <!-- ===========================
288 PDF UPLOAD (AJAX)
289 ============================ -->
290 <div class="wrap">
291 <h3>Sync Settings</h3>
292
293 <div class="mb-3">
294 <input type="checkbox" id="rag_auto_sync_enabled_tab" class="rag_auto_sync_enabled" <?php checked(get_option('rag_auto_sync_enabled'), '1'); ?>>
295 <label for="rag_auto_sync_enabled_tab"><strong>Enable Auto Sync (on Save)</strong></label>
296 <p class="description">Automatically update embeddings when a post or product is saved/updated.</p>
297 </div>
298 <hr>
299 <?php
300 $sync_interval = get_option('rag_sync_interval', 'daily');
301 $interval_labels = [
302 'hourly' => '(Hourly)',
303 'twicedaily' => '(Twice Daily)',
304 'daily' => '(Daily)',
305 'weekly' => '(Weekly)',
306 ];
307 $interval_desc = [
308 'hourly' => 'once per hour',
309 'twicedaily' => 'twice per day',
310 'daily' => 'once per day',
311 'weekly' => 'once per week',
312 ];
313 $current_label = $interval_labels[$sync_interval] ?? '(Daily)';
314 $current_desc = $interval_desc[$sync_interval] ?? 'once per day';
315 ?>
316
317 <div class="mb-3">
318 <input type="checkbox" id="rag_googlesheets_autosync_enabled" <?php checked(get_option('rag_googlesheets_autosync_enabled'), '1'); ?>>
319 <label for="rag_googlesheets_autosync_enabled"><strong>Enable Google Sheets Auto Sync <span class="rag-sync-label"><?php echo esc_html( $current_label ); ?></span></strong></label>
320 <p class="description">Automatically re-sync all Google Sheets in the knowledge base <span class="rag-sync-desc"><?php echo esc_html( $current_desc ); ?></span>.</p>
321 </div>
322
323 <div class="mb-3">
324 <input type="checkbox" id="rag_googledocs_autosync_enabled" <?php checked(get_option('rag_googledocs_autosync_enabled'), '1'); ?>>
325 <label for="rag_googledocs_autosync_enabled"><strong>Enable Google Docs Auto Sync <span class="rag-sync-label"><?php echo esc_html( $current_label ); ?></span></strong></label>
326 <p class="description">Automatically re-sync all Google Docs in the knowledge base <span class="rag-sync-desc"><?php echo esc_html( $current_desc ); ?></span>.</p>
327 </div>
328
329 <div class="mb-3">
330 <label for="rag_sync_interval"><strong>Sync Interval:</strong></label>
331 <select id="rag_sync_interval">
332 <option value="daily" <?php selected(get_option('rag_sync_interval', 'daily'), 'daily'); ?>>Once Daily</option>
333 <option value="twicedaily" <?php selected(get_option('rag_sync_interval'), 'twicedaily'); ?>>Twice Daily</option>
334 <option value="hourly" <?php selected(get_option('rag_sync_interval'), 'hourly'); ?>>Hourly</option>
335 <option value="weekly" <?php selected(get_option('rag_sync_interval'), 'weekly'); ?>>Weekly</option>
336 </select>
337 <p class="description">Choose how often to run the automated synchronization.</p>
338 </div>
339
340 </div>
341 <div class="wrap">
342 <h3>Upload PDF for RAG</h3>
343
344 <form id="rag-pdf-form">
345 <input type="file" id="rag-pdf-files" name="rag_pdf[]" multiple accept="application/pdf" />
346 <br><br>
347 <button type="submit" class="button button-primary" id="rag-pdf-submit">Upload & Embed PDF</button>
348 <button type="button" class="button" id="rag-media-pdf-btn">Select PDF from Media Library</button>
349 <button type="button" class="button" id="qcld-rag-list-pdf-btn">List PDF Files from Media Library</button>
350 <span id="rag-pdf-status" style="margin-left: 10px;"></span>
351 </form>
352
353 <div id="qcld-pdf-list-container" style="display:none; margin-top:20px; border:1px solid #ddd; padding:15px; background:#fff;">
354 <h4>Available PDF Files in Media Library</h4>
355 <div id="qcld-pdf-items" style="max-height:300px; overflow-y:auto; margin-bottom:15px;">
356 <p>Loading files...</p>
357 </div>
358 <button type="button" class="button button-primary qcld-rag-index-selected-media" data-type="pdf">Index Selected PDF Files</button>
359 <button type="button" class="button qcld-rag-close-media-list">Close List</button>
360 </div>
361
362 <div id="rag-pdf-output" style="margin-top: 15px;"></div>
363 </div>
364
365 <!-- ===========================
366 CSV UPLOAD (AJAX)
367 ============================ -->
368 <div class="wrap">
369 <h3>Upload CSV Data for RAG</h3>
370 <p>Upload CSV files with data to be embedded. Each row will be processed as a separate document. <a href="<?php echo esc_url( plugin_dir_url(__FILE__).'download/rag_test_data.csv' ); ?>">Download Test Data</a></p>
371
372 <form id="rag-csv-form">
373 <input type="file" id="rag-csv-files" name="rag_csv[]" multiple accept=".csv,text/csv" />
374 <br><br>
375 <button type="submit" class="button button-primary" id="rag-csv-submit">Upload & Embed CSV</button>
376 <span id="rag-csv-status" style="margin-left: 10px;"></span>
377 </form>
378
379 <div id="rag-csv-output" style="margin-top: 15px;"></div>
380 </div>
381
382 <!-- ===========================
383 DOCX UPLOAD (AJAX)
384 ============================ -->
385 <div class="wrap">
386 <h3>Upload Word (.docx) for RAG</h3>
387 <p>Upload MS Word files to be indexed in the knowledge base.</p>
388
389 <form id="rag-docx-form">
390 <input type="file" id="rag-docx-files" name="rag_docx[]" multiple accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
391 <br><br>
392 <button type="submit" class="button button-primary" id="rag-docx-submit">Upload & Embed Word</button>
393 <button type="button" class="button" id="rag-media-docx-btn">Select Word from Media Library</button>
394 <button type="button" class="button" id="qcld-rag-list-docx-btn">List Word Files from Media Library</button>
395 <span id="rag-docx-status" style="margin-left: 10px;"></span>
396 </form>
397
398 <div id="qcld-docx-list-container" style="display:none; margin-top:20px; border:1px solid #ddd; padding:15px; background:#fff;">
399 <h4>Available Word Files in Media Library</h4>
400 <div id="qcld-docx-items" style="max-height:300px; overflow-y:auto; margin-bottom:15px;">
401 <p>Loading files...</p>
402 </div>
403 <button type="button" class="button button-primary qcld-rag-index-selected-media" data-type="docx">Index Selected Word Files</button>
404 <button type="button" class="button qcld-rag-close-media-list">Close List</button>
405 </div>
406
407 <div id="rag-docx-output" style="margin-top: 15px;"></div>
408 </div>
409
410 <!-- ===========================
411 XAML UPLOAD (AJAX)
412 ============================ -->
413 <div class="wrap">
414 <h3>Upload XML Data for RAG</h3>
415 <p>Upload XML files with data to be embedded.</p>
416
417 <form id="rag-xaml-form">
418 <input type="file" id="rag-xaml-files" name="rag_xaml[]" multiple accept=".xaml,text/xml,application/xml" />
419 <br><br>
420 <button type="submit" class="button button-primary" id="rag-xaml-submit">Upload & Embed XAML</button>
421 <span id="rag-xaml-status" style="margin-left: 10px;"></span>
422 </form>
423
424 <div id="rag-xaml-output" style="margin-top: 15px;"></div>
425 </div>
426
427 <!-- ===========================
428 SITEMAP SUBMISSION
429 ============================ -->
430 <div class="wrap">
431 <h3>Submit Sitemap for RAG</h3>
432 <p>Enter your XML Sitemap URL to crawl and embed all pages.</p>
433 <input type="url" id="botmaster_sitemap_url" class="regular-text" placeholder="https://example.com/sitemap.xml" style="width: 100%; max-width: 400px;">
434 <button type="button" id="botmaster_submit_sitemap_btn" class="button button-primary">Process Sitemap</button>
435 <div id="botmaster_sitemap_status" style="margin-top: 10px;"></div>
436 </div>
437
438 <!-- ===========================
439 GOOGLE SHEETS SUBMISSION
440 ============================ -->
441 <div class="wrap">
442 <h3>Submit Google Sheet for RAG</h3>
443 <p>Enter your publicly published Google Sheet URL (Publish to web -> Web page).</p>
444 <input type="url" id="botmaster_googlesheet_url" class="regular-text" placeholder="https://docs.google.com/spreadsheets/d/e/.../pubhtml" style="width: 100%; max-width: 400px;">
445 <button type="button" id="botmaster_submit_googlesheet_btn" class="button button-primary">Process Google Sheet</button>
446
447 <div id="gs_progress_container" style="display:none; margin-top: 20px;">
448 <div class="progress" style="height: 20px; background-color: #f1f1f1; border-radius: 5px; overflow: hidden; border: 1px solid #ddd;">
449 <div id="gs_progress_bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%; height: 100%; background-color: #28a745; transition: width 0.3s ease;"></div>
450 </div>
451 <p id="gs_progress_status" style="margin-top: 10px; font-weight: bold; font-size: 13px;"></p>
452 </div>
453
454 <div id="botmaster_googlesheet_status" style="margin-top: 10px;"></div>
455 </div>
456
457 <!-- ===========================
458 GOOGLE DOCS SUBMISSION
459 ============================ -->
460 <div class="wrap">
461 <h3>Submit Google Doc for RAG</h3>
462 <p>Enter your publicly published Google Doc URL (File -> Share -> Publish to web).</p>
463 <input type="url" id="botmaster_googledoc_url" class="regular-text" placeholder="https://docs.google.com/document/d/.../pub" style="width: 100%; max-width: 400px;">
464 <button type="button" id="botmaster_submit_googledoc_btn" class="button button-primary">Process Google Doc</button>
465
466 <div id="gd_progress_container" style="display:none; margin-top: 20px;">
467 <div class="progress" style="height: 20px; background-color: #f1f1f1; border-radius: 5px; overflow: hidden; border: 1px solid #ddd;">
468 <div id="gd_progress_bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%; height: 100%; background-color: #007bff; transition: width 0.3s ease;"></div>
469 </div>
470 <p id="gd_progress_status" style="margin-top: 10px; font-weight: bold; font-size: 13px;"></p>
471 </div>
472
473 <div id="botmaster_googledoc_status" style="margin-top: 10px;"></div>
474 </div>
475 </div>
476 <h5 style="color: #c33;">Notes: These are based on AI system, so make sure you have enabled any AI features and set up valid API keys. <b><a href="<?php echo esc_url('https://wpbot.pro/docs/knowledgebase/how-to-use-an-embedded-vector-database-and-rag-to-get-customized-responses-from-ai/'); ?>" target="_blank">Check this Tutorial for more details.</a></b></h5>
477 <div class="wrap">
478 <button class="qcld-btn-primary" id="save_rag_setting">Save Settings</button>
479 </div>
480 </div>
481
482 <style>
483 .qcld-tab-content {
484 display: none !important;
485 }
486
487 .qcld-tab-content.active {
488 display: block !important;
489 }
490 </style>
491
492 <script>
493 jQuery(document).ready(function($) {
494 $('#botmaster_submit_sitemap_btn').on('click', function() {
495 var sitemapUrl = $('#botmaster_sitemap_url').val();
496 var statusDiv = $('#botmaster_sitemap_status');
497
498 if (!sitemapUrl) {
499 alert('Please enter a Sitemap URL');
500 return;
501 }
502
503 statusDiv.html('Processing... please wait.');
504 $(this).prop('disabled', true);
505
506 $.post(ajaxurl, {
507 action: 'botmaster_submit_sitemap',
508 sitemap_url: sitemapUrl,
509 nonce: '<?php echo esc_attr( wp_create_nonce('botmaster_kb_nonce') ); ?>'
510 }, function(response) {
511 $('#botmaster_submit_sitemap_btn').prop('disabled', false);
512 if (response.success) {
513 statusDiv.html('<div class="notice notice-success inline"><p>' + response.data.message + '</p></div>');
514 } else {
515 statusDiv.html('<div class="notice notice-error inline"><p>Error: ' + response.data + '</p></div>');
516 }
517 });
518 });
519 });
520 </script>
521 <div id="rag-sources" class="qcld-tab-content">
522 <?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'): ?>
523 <div class="wrap">
524 <div style="background-color: #fee; border: 1px solid #c33; padding: 15px; margin: 20px 0; border-radius: 4px;">
525 <p style="color: #c33; font-weight: bold; margin: 0; font-size: 14px;">
526 These options are available with the WPBot Pro <a href="<?php echo esc_url('https://www.wpbot.pro/pricing/'); ?>" target="_blank" style="color: #c33; text-decoration: underline;">Professional</a> and <a href="<?php echo esc_url('https://www.wpbot.pro/pricing/'); ?>" target="_blank" style="color: #c33; text-decoration: underline;">Master</a> Licenses
527 </p>
528 </div>
529 </div>
530 <?php endif; ?>
531
532 <div style="<?php if ( $wpchatbot_license_valid != 'master' && $wpchatbot_license_valid != 'professional'){ echo 'opacity:0.5; pointer-events:none;'; } ?>">
533 <div class="wrap">
534 <h3>Manage Knowledge Base Sources</h3>
535 <p>Manage entire files or spreadsheets instead of individual rows.</p>
536
537 <table class="wp-list-table widefat fixed striped">
538 <thead>
539 <tr>
540 <th class="check-column"><input type="checkbox" id="rag-sources-select-all"></th>
541 <th>Source Name / URL</th>
542 <th>Type</th>
543 <th>Documents</th>
544 <th>Actions</th>
545 </tr>
546 </thead>
547 <tbody id="rag-sources-list">
548 <?php
549 global $wpdb;
550 $table = $wpdb->prefix . 'rag_documents';
551 $sources = $wpdb->get_results("SELECT source_url, source_type, COUNT(*) as count, MAX(title) as display_name FROM {$table} GROUP BY source_url, source_type ORDER BY count DESC"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
552
553 if ($sources) {
554 foreach ($sources as $source) {
555 $display_url = $source->source_url ?: "Manual Upload";
556 ?>
557 <tr data-source-url="<?php echo esc_attr($source->source_url); ?>">
558 <th scope="row" class="check-column"><input type="checkbox" class="rag-source-checkbox" value="<?php echo esc_attr($source->source_url); ?>"></th>
559 <td>
560 <strong><?php echo esc_html($source->display_name); ?></strong><br>
561 <small style="color:#888;"><?php echo esc_html($display_url); ?></small>
562 </td>
563 <td><span class="badge" style="background:#eee; padding:2px 5px; border-radius:3px;"><?php echo esc_html($source->source_type); ?></span></td>
564 <td><?php echo esc_html($source->count); ?> chunks</td>
565 <td>
566 <button class="button rag-source-sync" data-url="<?php echo esc_attr($source->source_url); ?>" data-type="<?php echo esc_attr($source->source_type); ?>">Sync Now</button>
567 <button class="button button-link-delete rag-source-delete" data-url="<?php echo esc_attr($source->source_url); ?>">Delete All</button>
568 </td>
569 </tr>
570 <?php
571 }
572 } else {
573 echo '<tr><td colspan="5">No sources found.</td></tr>';
574 }
575 ?>
576 </tbody>
577 </table>
578
579 <div class="tablenav bottom">
580 <div class="alignleft actions bulkactions">
581 <select id="rag-sources-bulk-action">
582 <option value="-1">Bulk Actions</option>
583 <option value="delete">Delete Selected Sources</option>
584 </select>
585 <button type="button" id="rag-sources-apply-bulk" class="button action">Apply</button>
586 </div>
587 </div>
588 </div>
589
590 <div class="wrap" style="margin-top: 40px; border-top: 1px solid #ddd; padding-top: 20px;">
591 <h3>Database Utilities</h3>
592 <div class="notice notice-info inline">
593 <p>Upgrade your database to the new **High-Speed Binary Format**. This makes similarity searches significantly faster (10x+ speed improvement).</p>
594 </div>
595 <button type="button" id="rag-migrate-btn" class="button button-primary">Run High-Speed Migration</button>
596 <span id="rag-migrate-status" style="margin-left: 10px;"></span>
597 </div>
598 </div>
599 </div>
600
601
602
603
604 <div id="rag-database" class="qcld-tab-content">
605 <!-- ===========================
606 KNOWLEDGE BASE MANAGEMENT
607 ============================ -->
608 <div class="wrap">
609 <h3>Knowledge Base</h3>
610
611 <div class="tablenav top">
612 <div class="alignleft actions bulkactions">
613 <select id="rag-bulk-action-selector">
614 <option value="-1">Bulk Actions</option>
615 <option value="delete">Delete</option>
616 </select>
617 <button type="button" id="rag-apply-bulk-action" class="button action">Apply</button>
618 </div>
619 <div class="alignleft actions">
620 <button type="button" id="rag-delete-all" class="button button-link-delete" style="margin-left: 10px;">Delete All</button>
621
622
623 <div class="alignright actions" style="margin-left: 10px;">
624 <form method="get" style="display:inline-block;" action="?page=wpbot_openAi#ai-knowledge-base-tab#rag-database">
625 <input type="hidden" name="page" value="<?php echo esc_attr( sanitize_text_field( wp_unslash( $_GET['page'] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>">
626 <?php if (isset($_GET['post_type'])): // phpcs:ignore WordPress.Security.NonceVerification ?>
627 <input type="hidden" name="post_type" value="<?php echo esc_attr( sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>">
628 <?php endif; ?>
629 <p class="search-box" style="margin:0;">
630 <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...">
631 <input type="submit" id="search-submit" class="button" value="Search">
632 </p>
633 </form>
634 </div>
635 </div>
636
637 <br class="clear">
638 </div>
639 <?php
640 global $wpdb;
641 $table_rag_documents = $wpdb->prefix . 'rag_documents';
642
643 // Handle Table Creation
644 if (isset($_POST['rag_create_table']) && check_admin_referer('rag_create_table_nonce')) {
645 $charset = $wpdb->get_charset_collate();
646 $sql_rag_documents = "CREATE TABLE $table_rag_documents (
647 id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
648 doc_id VARCHAR(100) DEFAULT NULL,
649 title VARCHAR(255) NOT NULL,
650 content LONGTEXT NOT NULL,
651 embedding LONGTEXT NOT NULL,
652 source_type VARCHAR(20) DEFAULT 'post',
653 source_url VARCHAR(255) DEFAULT NULL,
654 file_url TEXT DEFAULT NULL,
655 metadata LONGTEXT DEFAULT NULL,
656 status VARCHAR(50) DEFAULT 'complete',
657 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
658 ) $charset;";
659 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
660 dbDelta($sql_rag_documents);
661 echo '<div class="notice notice-success inline"><p>Database table created successfully.</p></div>';
662 }
663
664 // Check if table exists
665 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $wpdb->esc_like($table_rag_documents))) === $table_rag_documents; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
666
667 if (!$table_exists) {
668 ?>
669 <div class="wrap">
670 <h3>Knowledge Base Database</h3>
671 <div class="notice notice-warning inline">
672 <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>
673 </div>
674 <form method="post">
675 <?php wp_nonce_field('rag_create_table_nonce'); ?>
676 <input type="hidden" name="rag_create_table" value="1">
677 <button type="submit" class="button button-primary">Create Database Table</button>
678 </form>
679 </div>
680 <?php
681 } else {
682 ?>
683 <?php
684 $table_name = $table_rag_documents;
685
686 $search_query = isset($_GET['s']) ? sanitize_text_field(wp_unslash($_GET['s'])) : '';
687
688 if ($search_query) {
689 $like = '%' . $wpdb->esc_like($search_query) . '%';
690 $total_items = $wpdb->get_var($wpdb->prepare("SELECT COUNT(id) FROM {$table_name} WHERE title LIKE %s OR content LIKE %s", $like, $like)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
691 } else {
692 $total_items = $wpdb->get_var("SELECT COUNT(id) FROM {$table_name}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
693 }
694 $items_per_page = 50;
695 $page = isset($_GET['paged']) ? absint(wp_unslash($_GET['paged'])) : 1;
696 $offset = ($page - 1) * $items_per_page;
697 $total_pages = ceil($total_items / $items_per_page);
698
699 $pagination_args = array(
700 'base' => add_query_arg(array('paged' => '%#%', 's' => $search_query), '?page=wpbot_openAi') . '#ai-knowledge-base-tab#rag-database',
701 'format' => '',
702 'prev_text' => __( '&laquo;', 'chatbot' ),
703 'next_text' => __( '&raquo;', 'chatbot' ),
704 'total' => $total_pages,
705 'current' => $page,
706 'type' => 'plain',
707 );
708
709 if ($total_pages > 1) {
710 // translators: %s: number of items.
711 echo '<div class="tablenav-pages"><span class="displaying-num">' . sprintf( esc_html( _n( '%s item', '%s items', $total_items, 'chatbot' ) ), esc_html( number_format_i18n($total_items) ) ) . '</span>';
712 echo wp_kses_post( paginate_links($pagination_args) );
713 echo '</div>';
714 }
715 ?>
716
717 <table class="wp-list-table widefat fixed striped">
718 <thead>
719 <tr>
720 <td id="cb" class="manage-column column-cb check-column">
721 <input type="checkbox" id="rag-select-all">
722 </td>
723 <th>Title</th>
724 <th>Content</th>
725 <th>Source Type</th>
726 <th>URL/File</th>
727 <th>Status</th>
728 <th>Actions</th>
729 </tr>
730 </thead>
731 <tbody id="rag-knowledge-base-list">
732 <?php
733 if ($search_query) {
734 $like = '%' . $wpdb->esc_like($search_query) . '%';
735 $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)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
736 } else {
737 $documents = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table_name} ORDER BY created_at DESC LIMIT %d OFFSET %d", $items_per_page, $offset)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
738 }
739
740 if ($documents) {
741 foreach ($documents as $doc) {
742 ?>
743 <tr id="rag-doc-<?php echo esc_attr( $doc->id ); ?>">
744 <th scope="qcld-row" class="check-column">
745 <input type="checkbox" class="rag-doc-checkbox" value="<?php echo esc_attr( $doc->id ); ?>">
746 </th>
747 <td><?php echo esc_html($doc->title); ?></td>
748 <td>
749 <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;">
750 <?php echo wp_kses_post($doc->content); ?>
751 </div>
752 </td>
753 <td><?php echo esc_html($doc->source_type); ?></td>
754 <td><?php echo esc_html($doc->source_url ?: $doc->file_url); ?></td>
755 <td><?php echo esc_html($doc->status); ?></td>
756 <td>
757 <?php if (!in_array($doc->source_type, ['csv', 'xml', 'xaml','sitemap'])): ?>
758 <button class="button button-small rag-sync-doc" data-id="<?php echo esc_attr( $doc->id ); ?>" title="Re-sync data from source">Sync</button>
759 <?php endif; ?>
760 <button class="button button-small rag-edit-doc" data-id="<?php echo esc_attr( $doc->id ); ?>">Edit</button>
761 <button class="button button-small button-link-delete rag-delete-doc" data-id="<?php echo esc_attr( $doc->id ); ?>">Delete</button>
762 </td>
763 </tr>
764 <?php
765 }
766 } else {
767 echo '<tr><td colspan="7">No documents found in knowledge base.</td></tr>';
768 }
769 ?>
770 </tbody>
771 </table>
772
773 <div class="tablenav bottom">
774 <?php
775 if ($total_pages > 1) {
776 // translators: %s: number of items.
777 echo '<div class="tablenav-pages"><span class="displaying-num">' . sprintf( esc_html( _n( '%s item', '%s items', $total_items, 'chatbot' ) ), esc_html( number_format_i18n($total_items) ) ) . '</span>';
778 echo wp_kses_post( paginate_links($pagination_args) );
779 echo '</div>';
780 }
781 ?>
782 <br class="clear">
783 </div>
784 <?php } ?>
785 </div>
786 </div>
787
788 <!-- Edit Document Modal -->
789 <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);">
790 <div style="background:#fff; margin:10% auto; padding:20px; width:60%; border-radius:5px;">
791 <h4>Edit Knowledge Base Document</h4>
792 <input type="hidden" id="edit-doc-id">
793 <div class="mb-3">
794 <label>Title</label><br>
795 <input type="text" id="edit-doc-title" class="regular-text" style="width:100%;">
796 </div>
797 <div class="mb-3">
798 <label>Content</label><br>
799 <textarea id="edit-doc-content" rows="10" style="width:100%;"></textarea>
800 </div>
801 <p class="description">Note: Updating content will re-generate embeddings.</p>
802 <div class="mt-3">
803 <button id="save-edit-doc" class="button button-primary">Save Changes</button>
804 <button id="close-edit-modal" class="button">Cancel</button>
805 </div>
806 </div>
807 </div>
808 </div>
809
810
811
812 <script>
813 jQuery(document).ready(function($) {
814 // PDF Upload Handler
815 $('#rag-pdf-form').on('submit', function(e) {
816 e.preventDefault();
817
818 var fileInput = $('#rag-pdf-files')[0];
819 if (!fileInput.files.length) {
820 alert('Please select PDF files to upload');
821 return;
822 }
823
824 var formData = new FormData();
825 for (var i = 0; i < fileInput.files.length; i++) {
826 formData.append('rag_pdf[]', fileInput.files[i]);
827 }
828 formData.append('action', 'rag_upload_pdf');
829 formData.append('nonce', '<?php echo esc_js( wp_create_nonce('rag_upload_nonce') ); ?>');
830
831 $('#rag-pdf-submit').prop('disabled', true);
832 $('#rag-pdf-status').html('<span class="spinner is-active" style="float:none;"></span> Uploading and processing...');
833 $('#rag-pdf-output').html('');
834
835 $.ajax({
836 url: ajaxurl,
837 type: 'POST',
838 data: formData,
839 processData: false,
840 contentType: false,
841 success: function(response) {
842 $('#rag-pdf-submit').prop('disabled', false);
843 if (response.success) {
844 $('#rag-pdf-status').html('<span style="color:green;">✓ Complete</span>');
845 $('#rag-pdf-output').html(response.data.output);
846 $('#rag-pdf-files').val('');
847 } else {
848 $('#rag-pdf-status').html('<span style="color:red;">✗ Error</span>');
849 $('#rag-pdf-output').html('<p style="color:red;">' + response.data.message + '</p>');
850 }
851 },
852 error: function(xhr) {
853 $('#rag-pdf-submit').prop('disabled', false);
854 $('#rag-pdf-status').html('<span style="color:red;">✗ Error</span>');
855 $('#rag-pdf-output').html('<p style="color:red;">Upload failed. Please try again.</p>');
856 }
857 });
858 });
859
860 // CSV Upload Handler
861 $('#rag-csv-form').on('submit', function(e) {
862 e.preventDefault();
863
864 var fileInput = $('#rag-csv-files')[0];
865 if (!fileInput.files.length) {
866 alert('Please select CSV files to upload');
867 return;
868 }
869
870 var formData = new FormData();
871 for (var i = 0; i < fileInput.files.length; i++) {
872 formData.append('rag_csv[]', fileInput.files[i]);
873 }
874 formData.append('action', 'rag_upload_csv');
875 formData.append('nonce', '<?php echo esc_js( wp_create_nonce('rag_upload_nonce') ); ?>');
876
877 $('#rag-csv-submit').prop('disabled', true);
878 $('#rag-csv-status').html('<span class="spinner is-active" style="float:none;"></span> Uploading and processing...');
879 $('#rag-csv-output').html('');
880
881 $.ajax({
882 url: ajaxurl,
883 type: 'POST',
884 data: formData,
885 processData: false,
886 contentType: false,
887 success: function(response) {
888 $('#rag-csv-submit').prop('disabled', false);
889 if (response.success) {
890 $('#rag-csv-status').html('<span style="color:green;">✓ Complete</span>');
891 $('#rag-csv-output').html(response.data.output);
892 $('#rag-csv-files').val('');
893 } else {
894 $('#rag-csv-status').html('<span style="color:red;">✗ Error</span>');
895 $('#rag-csv-output').html('<p style="color:red;">' + response.data.message + '</p>');
896 }
897 },
898 error: function(xhr) {
899 $('#rag-csv-submit').prop('disabled', false);
900 $('#rag-csv-status').html('<span style="color:red;">✗ Error</span>');
901 $('#rag-csv-output').html('<p style="color:red;">Upload failed. Please try again.</p>');
902 }
903 });
904 });
905
906 // XAML Upload Handler
907 $('#rag-xaml-form').on('submit', function(e) {
908 e.preventDefault();
909
910 var fileInput = $('#rag-xaml-files')[0];
911 if (!fileInput.files.length) {
912 alert('Please select XAML files to upload');
913 return;
914 }
915
916 var formData = new FormData();
917 for (var i = 0; i < fileInput.files.length; i++) {
918 formData.append('rag_xaml[]', fileInput.files[i]);
919 }
920 formData.append('action', 'rag_upload_xaml');
921 formData.append('nonce', '<?php echo esc_js( wp_create_nonce('rag_upload_nonce') ); ?>');
922
923 $('#rag-xaml-submit').prop('disabled', true);
924 $('#rag-xaml-status').html('<span class="spinner is-active" style="float:none;"></span> Uploading and processing...');
925 $('#rag-xaml-output').html('');
926
927 $.ajax({
928 url: ajaxurl,
929 type: 'POST',
930 data: formData,
931 processData: false,
932 contentType: false,
933 success: function(response) {
934 $('#rag-xaml-submit').prop('disabled', false);
935 if (response.success) {
936 $('#rag-xaml-status').html('<span style="color:green;">✓ Complete</span>');
937 $('#rag-xaml-output').html(response.data.output);
938 $('#rag-xaml-files').val('');
939 } else {
940 $('#rag-xaml-status').html('<span style="color:red;">✗ Error</span>');
941 $('#rag-xaml-output').html('<p style="color:red;">' + response.data.message + '</p>');
942 }
943 },
944 error: function(xhr) {
945 $('#rag-xaml-submit').prop('disabled', false);
946 $('#rag-xaml-status').html('<span style="color:red;">✗ Error</span>');
947 $('#rag-xaml-output').html('<p style="color:red;">Upload failed. Please try again.</p>');
948 }
949 });
950 });
951 });
952 </script>
953 <script>
954 jQuery(document).ready(function($) {
955 // Tab switching logic
956 $('.nav-tab-wrapper').on('click', '.nav-tab', function(e) {
957 var $tab = $(this);
958 var targetId = $tab.attr('href');
959 var $content = $(targetId);
960
961 if ($tab.data('disabled')) {
962 e.preventDefault();
963 return false;
964 }
965
966 if ($content.length) {
967 e.preventDefault();
968 // Update tabs
969 $('.nav-tab').removeClass('nav-tab-active');
970 $tab.addClass('nav-tab-active');
971
972 // Update content area
973 $('.qcld-tab-content').removeClass('active');
974 $content.addClass('active');
975
976 // Update URL hash without jumping
977 var compositeHash = '#ai-knowledge-base-tab' + targetId;
978 if (history.pushState) {
979 history.pushState(null, null, compositeHash);
980 } else {
981 window.location.hash = compositeHash;
982 }
983 }
984 });
985
986 // Handle hash on page load
987 var hash = window.location.hash;
988 if (hash && hash.includes('#rag-')) {
989 var subTabHash = '#' + hash.split('#').pop();
990 var $targetTab = $('.nav-tab-wrapper a[href="' + subTabHash + '"]');
991 if ($targetTab.length) {
992 $targetTab.trigger('click');
993 }
994 } else if (hash === '#ai-knowledge-base-tab') {
995 // Default to first tab if no sub-tab specified
996 $('.nav-tab-wrapper a').first().trigger('click');
997 }
998 });
999 </script>