PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.16
MxChat – AI Chatbot & Content Generation for WordPress v3.2.16
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / admin-settings-page.php

admin-settings-page.php in MxChat – AI Chatbot & Content Generation for WordPress 3.2.16, at includes/admin-settings-page.php

1,873 lines 131.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MxChat Admin Settings Page - Redesigned with Sidebar Navigation
4 *
5 * @package MxChat
6 * @since 2.7.0
7 */
8
9 if (!defined('ABSPATH')) {
10 exit;
11 }
12
13 /**
14 * Render the new sidebar-based admin settings page
15 */
16 function mxchat_render_settings_page($admin_instance) {
17 $is_activated = $admin_instance->is_activated();
18 $options = get_option('mxchat_options', array());
19 $plugin_url = plugin_dir_url(dirname(__FILE__));
20 ?>
21 <div class="mxch-admin-wrapper">
22 <!-- Mobile Header -->
23 <header class="mxch-mobile-header">
24 <a href="#" class="mxch-mobile-logo">
25 <div class="mxch-mobile-logo-icon">
26 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
27 </div>
28 <span class="mxch-mobile-logo-text">MxChat</span>
29 </a>
30 <button type="button" class="mxch-mobile-menu-btn" aria-label="<?php esc_attr_e('Open menu', 'mxchat'); ?>">
31 <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"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
32 </button>
33 </header>
34
35 <!-- Mobile Menu Overlay -->
36 <div class="mxch-mobile-overlay"></div>
37
38 <!-- Mobile Menu Modal -->
39 <div class="mxch-mobile-menu">
40 <div class="mxch-mobile-menu-header">
41 <span class="mxch-mobile-menu-title"><?php esc_html_e('Settings', 'mxchat'); ?></span>
42 <button type="button" class="mxch-mobile-menu-close" aria-label="<?php esc_attr_e('Close menu', 'mxchat'); ?>">
43 <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"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
44 </button>
45 </div>
46 <nav class="mxch-mobile-menu-nav">
47 <!-- Configuration Section -->
48 <div class="mxch-mobile-nav-section">
49 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('Configuration', 'mxchat'); ?></div>
50 <button class="mxch-mobile-nav-link" data-parent="chatbot">
51 <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"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
52 <span><?php esc_html_e('Chatbot', 'mxchat'); ?></span>
53 <svg class="mxch-mobile-nav-arrow" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
54 </button>
55 <div class="mxch-mobile-nav-sub" data-parent="chatbot">
56 <button class="mxch-mobile-nav-sub-link active" data-target="chatbot-ai-models"><?php esc_html_e('AI Models', 'mxchat'); ?></button>
57 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-behavior"><?php esc_html_e('Behavior', 'mxchat'); ?></button>
58 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-display"><?php esc_html_e('Display', 'mxchat'); ?></button>
59 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-lead-capture"><?php esc_html_e('Lead Capture', 'mxchat'); ?></button>
60 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-quick-questions"><?php esc_html_e('Quick Questions', 'mxchat'); ?></button>
61 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-rate-limits"><?php esc_html_e('Rate Limits', 'mxchat'); ?></button>
62 </div>
63 <button class="mxch-mobile-nav-link" data-target="api-keys">
64 <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"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
65 <span><?php esc_html_e('API Keys', 'mxchat'); ?></span>
66 </button>
67 <button class="mxch-mobile-nav-link" data-target="optimization">
68 <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"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
69 <span><?php esc_html_e('Optimization', 'mxchat'); ?></span>
70 </button>
71 <button class="mxch-mobile-nav-link" data-target="testing">
72 <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"><path d="M9 2v6"/><path d="M15 2v6"/><path d="M12 17v5"/><path d="M5 8h14"/><path d="M6 11v-1a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/><path d="m9 14 3 3 3-3"/></svg>
73 <span><?php esc_html_e('Testing', 'mxchat'); ?></span>
74 </button>
75 </div>
76 <!-- Integrations Section -->
77 <div class="mxch-mobile-nav-section">
78 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('Integrations', 'mxchat'); ?></div>
79 <button class="mxch-mobile-nav-link" data-parent="integrations">
80 <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"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
81 <span><?php esc_html_e('Integrations', 'mxchat'); ?></span>
82 <svg class="mxch-mobile-nav-arrow" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
83 </button>
84 <div class="mxch-mobile-nav-sub" data-parent="integrations">
85 <button class="mxch-mobile-nav-sub-link" data-target="integrations-toolbar"><?php esc_html_e('Toolbar', 'mxchat'); ?></button>
86 <button class="mxch-mobile-nav-sub-link" data-target="integrations-loops"><?php esc_html_e('Loops', 'mxchat'); ?></button>
87 <button class="mxch-mobile-nav-sub-link" data-target="integrations-brave"><?php esc_html_e('Brave Search', 'mxchat'); ?></button>
88 <button class="mxch-mobile-nav-sub-link" data-target="integrations-slack"><?php esc_html_e('Slack', 'mxchat'); ?></button>
89 <button class="mxch-mobile-nav-sub-link" data-target="integrations-telegram"><?php esc_html_e('Telegram', 'mxchat'); ?></button>
90 </div>
91 </div>
92 <!-- Resources Section -->
93 <div class="mxch-mobile-nav-section">
94 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('Resources', 'mxchat'); ?></div>
95 <button class="mxch-mobile-nav-link" data-target="tutorials">
96 <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"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
97 <span><?php esc_html_e('Tutorials', 'mxchat'); ?></span>
98 </button>
99 </div>
100 <?php if (!$is_activated): ?>
101 <div class="mxch-mobile-menu-footer">
102 <a href="https://mxchat.ai/" target="_blank" class="mxch-mobile-upgrade-btn">
103 <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"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/></svg>
104 <?php esc_html_e('Upgrade to Pro', 'mxchat'); ?>
105 </a>
106 </div>
107 <?php endif; ?>
108 </nav>
109 </div>
110
111 <!-- Sidebar Navigation -->
112 <aside class="mxch-sidebar">
113 <div class="mxch-sidebar-header">
114 <a href="#" class="mxch-sidebar-logo">
115 <div class="mxch-sidebar-logo-icon">
116 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
117 </div>
118 <span class="mxch-sidebar-logo-text">MxChat</span>
119 <span class="mxch-sidebar-version">v<?php echo esc_html(MXCHAT_VERSION ?? '2.7.0'); ?></span>
120 </a>
121 </div>
122
123 <nav class="mxch-sidebar-nav">
124 <!-- Chatbot Section -->
125 <div class="mxch-nav-section">
126 <div class="mxch-nav-section-title"><?php esc_html_e('Configuration', 'mxchat'); ?></div>
127
128 <div class="mxch-nav-item expanded" data-section="chatbot">
129 <button class="mxch-nav-link active">
130 <span class="mxch-nav-link-icon">
131 <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"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
132 </span>
133 <span class="mxch-nav-link-text"><?php esc_html_e('Chatbot', 'mxchat'); ?></span>
134 <svg class="mxch-nav-link-arrow" 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"><polyline points="9 18 15 12 9 6"></polyline></svg>
135 </button>
136 <div class="mxch-nav-sub">
137 <button class="mxch-nav-sub-link active" data-target="chatbot-ai-models"><?php esc_html_e('AI Models', 'mxchat'); ?></button>
138 <button class="mxch-nav-sub-link" data-target="chatbot-behavior"><?php esc_html_e('Behavior', 'mxchat'); ?></button>
139 <button class="mxch-nav-sub-link" data-target="chatbot-display"><?php esc_html_e('Display', 'mxchat'); ?></button>
140 <button class="mxch-nav-sub-link" data-target="chatbot-lead-capture"><?php esc_html_e('Lead Capture', 'mxchat'); ?></button>
141 <button class="mxch-nav-sub-link" data-target="chatbot-quick-questions"><?php esc_html_e('Quick Questions', 'mxchat'); ?></button>
142 <button class="mxch-nav-sub-link" data-target="chatbot-rate-limits"><?php esc_html_e('Rate Limits', 'mxchat'); ?></button>
143 </div>
144 </div>
145
146 <div class="mxch-nav-item" data-section="api-keys">
147 <button class="mxch-nav-link" data-target="api-keys">
148 <span class="mxch-nav-link-icon">
149 <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"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
150 </span>
151 <span class="mxch-nav-link-text"><?php esc_html_e('API Keys', 'mxchat'); ?></span>
152 </button>
153 </div>
154
155 <div class="mxch-nav-item" data-section="optimization">
156 <button class="mxch-nav-link" data-target="optimization">
157 <span class="mxch-nav-link-icon">
158 <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"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
159 </span>
160 <span class="mxch-nav-link-text"><?php esc_html_e('Optimization', 'mxchat'); ?></span>
161 </button>
162 </div>
163
164 <div class="mxch-nav-item" data-section="testing">
165 <button class="mxch-nav-link" data-target="testing">
166 <span class="mxch-nav-link-icon">
167 <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"><path d="M9 2v6"/><path d="M15 2v6"/><path d="M12 17v5"/><path d="M5 8h14"/><path d="M6 11v-1a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/><path d="m9 14 3 3 3-3"/></svg>
168 </span>
169 <span class="mxch-nav-link-text"><?php esc_html_e('Testing', 'mxchat'); ?></span>
170 </button>
171 </div>
172 </div>
173
174 <!-- Integrations Section -->
175 <div class="mxch-nav-section">
176 <div class="mxch-nav-section-title"><?php esc_html_e('Integrations', 'mxchat'); ?></div>
177
178 <div class="mxch-nav-item" data-section="integrations">
179 <button class="mxch-nav-link">
180 <span class="mxch-nav-link-icon">
181 <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"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
182 </span>
183 <span class="mxch-nav-link-text"><?php esc_html_e('Integrations', 'mxchat'); ?></span>
184 <svg class="mxch-nav-link-arrow" 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"><polyline points="9 18 15 12 9 6"></polyline></svg>
185 </button>
186 <div class="mxch-nav-sub">
187 <button class="mxch-nav-sub-link" data-target="integrations-toolbar"><?php esc_html_e('Toolbar', 'mxchat'); ?></button>
188 <button class="mxch-nav-sub-link" data-target="integrations-loops"><?php esc_html_e('Loops', 'mxchat'); ?></button>
189 <button class="mxch-nav-sub-link" data-target="integrations-brave"><?php esc_html_e('Brave Search', 'mxchat'); ?></button>
190 <button class="mxch-nav-sub-link" data-target="integrations-slack"><?php esc_html_e('Slack', 'mxchat'); ?></button>
191 <button class="mxch-nav-sub-link" data-target="integrations-telegram"><?php esc_html_e('Telegram', 'mxchat'); ?></button>
192 </div>
193 </div>
194 </div>
195
196 <!-- Resources Section -->
197 <div class="mxch-nav-section">
198 <div class="mxch-nav-section-title"><?php esc_html_e('Resources', 'mxchat'); ?></div>
199
200 <div class="mxch-nav-item" data-section="tutorials">
201 <button class="mxch-nav-link" data-target="tutorials">
202 <span class="mxch-nav-link-icon">
203 <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"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
204 </span>
205 <span class="mxch-nav-link-text"><?php esc_html_e('Tutorials', 'mxchat'); ?></span>
206 </button>
207 </div>
208 </div>
209
210 </nav>
211
212 <?php if (!$is_activated): ?>
213 <div class="mxch-sidebar-footer">
214 <a href="https://mxchat.ai/" target="_blank" class="mxch-sidebar-upgrade-v2">
215 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
216 <?php esc_html_e('Pro Upgrade', 'mxchat'); ?>
217 </a>
218 </div>
219 <?php endif; ?>
220 </aside>
221
222 <!-- Main Content Area -->
223 <main class="mxch-content">
224 <?php if (!$is_activated): ?>
225 <div class="mxch-pro-banner">
226 <div class="mxch-pro-banner-content">
227 <h3 class="mxch-pro-banner-title"><?php esc_html_e('Stop paying $50–200/month for AI chatbot tools.', 'mxchat'); ?></h3>
228 <p class="mxch-pro-banner-text"><?php esc_html_e('One payment unlocks every MxChat Pro add-on for life — MCP server access for Claude / ChatGPT / Claude Code, WooCommerce sales, Google Search Console insights, AI-generated chatbot themes, lead-capture forms, unlimited bots, image analysis, and more. No renewals. No surprise bills.', 'mxchat'); ?></p>
229 </div>
230 <a href="https://mxchat.ai/" target="_blank" class="mxch-pro-banner-btn"><span><?php esc_html_e('Get Lifetime Access →', 'mxchat'); ?></span></a>
231 </div>
232 <?php endif; ?>
233
234 <?php
235 // Show theme migration notice for Pro users
236 $admin_instance->show_theme_migration_banner();
237 ?>
238
239 <!-- ========================================
240 CHATBOT - AI MODELS
241 ======================================== -->
242 <div id="chatbot-ai-models" class="mxch-section active">
243 <div class="mxch-content-header">
244 <h1 class="mxch-content-title"><?php esc_html_e('AI Models', 'mxchat'); ?></h1>
245 <p class="mxch-content-subtitle"><?php esc_html_e('Configure the AI models your chatbot will use for conversations and embeddings.', 'mxchat'); ?></p>
246 </div>
247
248 <div class="mxch-card">
249 <div class="mxch-card-body mxchat-autosave-section">
250 <?php
251 // Chat Model
252 mxchat_render_field_wrapper('model', __('Chat Model', 'mxchat'), function() use ($admin_instance) {
253 $admin_instance->mxchat_model_callback();
254 }, __('Select the AI model your chatbot will use for conversations.', 'mxchat'));
255
256 // Embedding Model
257 mxchat_render_field_wrapper('embedding_model', __('Embedding Model', 'mxchat'), function() use ($admin_instance) {
258 $admin_instance->embedding_model_callback();
259 }, __('Vector embedding model for knowledge base matching. Changing models requires reconfiguring knowledge data.', 'mxchat'));
260
261 // Enable Streaming
262 mxchat_render_field_wrapper('enable_streaming', __('Enable Streaming', 'mxchat'), function() use ($admin_instance) {
263 $admin_instance->enable_streaming_toggle_callback();
264 }, __('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, Grok).', 'mxchat'));
265
266 // Enable Web Search (OpenAI & Gemini)
267 mxchat_render_field_wrapper('enable_web_search', __('Enable Web Search', 'mxchat'), function() use ($admin_instance) {
268 $admin_instance->enable_web_search_toggle_callback();
269 }, __('Allow the chatbot to search the web for current information (OpenAI and Gemini models).', 'mxchat'));
270 ?>
271 </div>
272 </div>
273 </div>
274
275 <!-- ========================================
276 CHATBOT - BEHAVIOR
277 ======================================== -->
278 <div id="chatbot-behavior" class="mxch-section">
279 <div class="mxch-content-header">
280 <h1 class="mxch-content-title"><?php esc_html_e('Behavior', 'mxchat'); ?></h1>
281 <p class="mxch-content-subtitle"><?php esc_html_e('Control how your AI chatbot responds and processes queries.', 'mxchat'); ?></p>
282 </div>
283
284 <div class="mxch-card">
285 <div class="mxch-card-body mxchat-autosave-section">
286 <?php
287 // AI Instructions
288 mxchat_render_field_wrapper('system_prompt_instructions', __('AI Instructions (Behavior)', 'mxchat'), function() use ($admin_instance) {
289 $admin_instance->system_prompt_instructions_callback();
290 }, __('Provide system-level instructions to guide the AI\'s behavior and responses.', 'mxchat'));
291
292 // Similarity Threshold
293 mxchat_render_field_wrapper('similarity_threshold', __('Similarity Threshold', 'mxchat'), function() use ($admin_instance) {
294 $admin_instance->mxchat_similarity_threshold_callback();
295 }, __('Adjust threshold for content matching. Lower values = more matches, higher values = stricter matching.', 'mxchat'));
296
297 // RAG Sources Limit
298 mxchat_render_field_wrapper('rag_sources_limit', __('RAG Sources Limit', 'mxchat'), function() use ($admin_instance) {
299 $admin_instance->mxchat_rag_sources_limit_callback();
300 }, __('Number of knowledge base sources (pages/documents) to include in AI context. Higher values provide more context but use more tokens.', 'mxchat'));
301
302 // RAG Chunks Limit
303 mxchat_render_field_wrapper('rag_chunks_limit', __('RAG Chunks Limit', 'mxchat'), function() use ($admin_instance) {
304 $admin_instance->mxchat_rag_chunks_limit_callback();
305 }, __('Maximum total content chunks sent to the AI across all sources. Higher values provide more context but increase token usage and cost.', 'mxchat'));
306
307 // Hybrid keyword boost (plan-38ffa1). Standalone option — never part
308 // of mxchat_options, so mxchat_sanitize() can't strip it. Default OFF.
309 $hybrid_keyword = get_option('mxchat_hybrid_keyword_toggle', 'off');
310 ?>
311 <div class="mxch-field" style="margin-top: 16px;">
312 <label class="mxch-toggle">
313 <input type="checkbox" class="mxch-toggle-input mxchat-autosave-field" id="mxchat_hybrid_keyword_toggle" name="mxchat_hybrid_keyword_toggle" value="on" <?php checked($hybrid_keyword, 'on'); ?>>
314 <span class="mxch-toggle-switch"></span>
315 <span class="mxch-toggle-label"><?php esc_html_e('Hybrid keyword boost (WP-DB knowledge base)', 'mxchat'); ?></span>
316 </label>
317 <p class="mxch-field-description"><?php esc_html_e('Combines keyword matching with vector similarity when searching your WordPress-database knowledge base. Helps exact-token questions — product codes, SKUs, error codes, names — find the right document even when semantic similarity alone would miss it.', 'mxchat'); ?></p>
318 <p class="mxch-field-hint"><?php esc_html_e('Only affects the WordPress-database knowledge base; Pinecone is unaffected. Changes which sources are retrieved on existing installs, so test after enabling. The Transcripts sources panel labels how each match was found while this is on.', 'mxchat'); ?></p>
319 </div>
320 <?php
321
322 // Contextual Awareness
323 mxchat_render_field_wrapper('contextual_awareness', __('Contextual Awareness', 'mxchat'), function() use ($admin_instance) {
324 $admin_instance->mxchat_contextual_awareness_callback();
325 }, __('Allow the chatbot to understand and reference the current page content for more relevant responses.', 'mxchat'));
326
327 // Citation Links
328 mxchat_render_field_wrapper('citation_links', __('Citation Links', 'mxchat'), function() use ($admin_instance) {
329 $admin_instance->mxchat_citation_links_toggle_callback();
330 }, __('Allow the AI to include citation links from your knowledge database in responses. If disabled, ensure your AI Behavior settings do not mention links or the AI may fabricate URLs.', 'mxchat'));
331
332 // Satisfaction Rating Prompt
333 // The toggle callback inline-renders the 5 customization
334 // fields inside a single sub-options wrapper (plan-29caac).
335 // No separate customization group needed — the wrapper handles
336 // its own server-side display state based on the toggle value.
337 mxchat_render_field_wrapper('satisfaction_rating', __('Satisfaction Rating Prompt', 'mxchat'), function() use ($admin_instance) {
338 $admin_instance->mxchat_satisfaction_rating_toggle_callback();
339 }, __('Show a 👍 / 👎 prompt after a conversation has had a couple of bot replies and the visitor has been idle for a moment. Disable to hide the prompt site-wide.', 'mxchat'));
340
341 // Editor Assistant — free, off-by-default block-editor AI actions (plan-8cb0cb).
342 mxchat_render_field_wrapper('mxchat_editor_assistant_enabled', __('Editor Assistant', 'mxchat'), function() use ($admin_instance) {
343 $admin_instance->mxchat_editor_assistant_toggle_callback();
344 }, __('Add magic-wand AI writing actions (rewrite, summarize, translate, continue, fix grammar) to the WordPress block editor. Opens a sidebar in the post editor. Uses the AI model configured above. Off by default.', 'mxchat'));
345 ?>
346 </div>
347 </div>
348 </div>
349
350 <!-- ========================================
351 CHATBOT - DISPLAY
352 ======================================== -->
353 <div id="chatbot-display" class="mxch-section">
354 <div class="mxch-content-header">
355 <h1 class="mxch-content-title"><?php esc_html_e('Display Settings', 'mxchat'); ?></h1>
356 <p class="mxch-content-subtitle"><?php esc_html_e('Customize how and where your chatbot appears on your website.', 'mxchat'); ?></p>
357 </div>
358
359 <div class="mxch-card">
360 <div class="mxch-card-header">
361 <h3 class="mxch-card-title">
362 <svg class="mxch-card-title-icon" 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"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
363 <?php esc_html_e('Visibility', 'mxchat'); ?>
364 </h3>
365 </div>
366 <div class="mxch-card-body mxchat-autosave-section">
367 <?php
368 // Auto-Display
369 mxchat_render_field_wrapper('auto_display', __('Auto-Display Chatbot', 'mxchat'), function() use ($admin_instance) {
370 $admin_instance->mxchat_append_to_body_callback();
371 }, __('Show chatbot automatically on all pages. Disable to use shortcode [mxchat_chatbot floating="yes"] for floating widget or [mxchat_chatbot floating="no"] for embedded chat.', 'mxchat'));
372
373 // Open Links in New Tab
374 mxchat_render_field_wrapper('link_target', __('Open Links in New Tab', 'mxchat'), function() use ($admin_instance) {
375 $admin_instance->mxchat_link_target_toggle_callback();
376 }, __('Open links clicked in chat responses in a new browser tab.', 'mxchat'));
377
378 // Chat Persistence
379 mxchat_render_field_wrapper('chat_persistence', __('Chat Persistence', 'mxchat'), function() use ($admin_instance) {
380 $admin_instance->mxchat_chat_persistence_toggle_callback();
381 }, __('Keep chat history when users navigate or return within 24 hours.', 'mxchat'));
382
383 // Download Transcript button
384 mxchat_render_field_wrapper('print_button', __('Show Download Transcript Button', 'mxchat'), function() use ($admin_instance) {
385 $admin_instance->mxchat_print_button_toggle_callback();
386 }, __('Show the "Download Transcript" item in the chat window menu.', 'mxchat'));
387
388 // Start-New-Chat button (plan ac2e81) — lets a visitor reset the
389 // conversation without the owner disabling chat persistence globally.
390 mxchat_render_field_wrapper('reset_chat_enabled', __('Show Start-New-Chat Button', 'mxchat'), function() use ($admin_instance) {
391 $admin_instance->mxchat_reset_chat_toggle_callback();
392 }, __('Show a "Start new chat" item in the chat window menu that clears the current conversation and begins a fresh session.', 'mxchat'));
393
394 mxchat_render_field_wrapper('reset_chat_label', __('Start-New-Chat Button Label', 'mxchat'), function() use ($admin_instance) {
395 $admin_instance->mxchat_reset_chat_label_callback();
396 }, __('Label for the Start-New-Chat menu item. Leave blank to use "Start new chat".', 'mxchat'));
397 ?>
398 </div>
399 </div>
400
401 <div class="mxch-card">
402 <div class="mxch-card-header">
403 <h3 class="mxch-card-title">
404 <svg class="mxch-card-title-icon" 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"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
405 <?php esc_html_e('Text & Labels', 'mxchat'); ?>
406 </h3>
407 </div>
408 <div class="mxch-card-body mxchat-autosave-section">
409 <?php
410 // Top Bar Title
411 mxchat_render_field_wrapper('top_bar_title', __('Top Bar Title', 'mxchat'), function() use ($admin_instance) {
412 $admin_instance->mxchat_top_bar_title_callback();
413 }, __('Title text shown in the chatbot header.', 'mxchat'));
414
415 // AI Agent Text
416 mxchat_render_field_wrapper('ai_agent_text', __('AI Agent Text', 'mxchat'), function() use ($admin_instance) {
417 $admin_instance->mxchat_ai_agent_text_callback();
418 }, __('Text shown for AI agents in the status indicator.', 'mxchat'));
419
420 // Intro Message
421 mxchat_render_field_wrapper('intro_message', __('Introductory Message', 'mxchat'), function() use ($admin_instance) {
422 $admin_instance->mxchat_intro_message_callback();
423 }, __('First message shown when chat opens. HTML allowed.', 'mxchat'));
424
425 // Input Placeholder
426 mxchat_render_field_wrapper('input_copy', __('Input Placeholder', 'mxchat'), function() use ($admin_instance) {
427 $admin_instance->mxchat_input_copy_callback();
428 }, __('Placeholder text in the chat input field.', 'mxchat'));
429
430 // Max Input Length (characters) — plan a3fae2 part C
431 mxchat_render_field_wrapper('max_input_length', __('Max Input Length (characters)', 'mxchat'), function() use ($admin_instance) {
432 $admin_instance->mxchat_max_input_length_callback();
433 }, __('Cap how many characters a visitor can type or paste into a single message. Set to 0 for unlimited (default).', 'mxchat'));
434
435 // Chat Teaser
436 mxchat_render_field_wrapper('pre_chat_message', __('Chat Teaser Pop-up', 'mxchat'), function() use ($admin_instance) {
437 $admin_instance->mxchat_pre_chat_message_callback();
438 }, __('Message shown before users start chatting.', 'mxchat'));
439 ?>
440 </div>
441 </div>
442 </div>
443
444 <!-- ========================================
445 CHATBOT - LEAD CAPTURE
446 ======================================== -->
447 <div id="chatbot-lead-capture" class="mxch-section">
448 <div class="mxch-content-header">
449 <h1 class="mxch-content-title"><?php esc_html_e('Lead Capture', 'mxchat'); ?></h1>
450 <p class="mxch-content-subtitle"><?php esc_html_e('Collect user information and manage privacy settings.', 'mxchat'); ?></p>
451 </div>
452
453 <div class="mxch-card">
454 <div class="mxch-card-header">
455 <h3 class="mxch-card-title">
456 <svg class="mxch-card-title-icon" 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"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
457 <?php esc_html_e('Email Capture', 'mxchat'); ?>
458 </h3>
459 </div>
460 <div class="mxch-card-body mxchat-autosave-section">
461 <?php
462 // Require Email
463 mxchat_render_field_wrapper('enable_email_block', __('Require Email to Chat', 'mxchat'), function() use ($admin_instance) {
464 $admin_instance->enable_email_block_callback();
465 }, __('Require users to enter their email before chatting. Shows for non-logged-in users.', 'mxchat'));
466
467 // Email Form Content
468 mxchat_render_field_wrapper('email_blocker_header', __('Email Form Content', 'mxchat'), function() use ($admin_instance) {
469 $admin_instance->email_blocker_header_content_callback();
470 }, __('HTML content shown above the email form.', 'mxchat'));
471
472 // Email Button Text
473 mxchat_render_field_wrapper('email_blocker_button', __('Submit Button Text', 'mxchat'), function() use ($admin_instance) {
474 $admin_instance->email_blocker_button_text_callback();
475 }, __('Text for the email form submit button.', 'mxchat'));
476
477 // Require Name
478 mxchat_render_field_wrapper('enable_name_field', __('Also Require Name', 'mxchat'), function() use ($admin_instance) {
479 $admin_instance->enable_name_field_callback();
480 }, __('Also require users to enter their name with email.', 'mxchat'));
481
482 // Name Placeholder
483 mxchat_render_field_wrapper('name_placeholder', __('Name Field Placeholder', 'mxchat'), function() use ($admin_instance) {
484 $admin_instance->name_field_placeholder_callback();
485 }, __('Placeholder text for the name input field.', 'mxchat'));
486 ?>
487 </div>
488 </div>
489
490 <div class="mxch-card">
491 <div class="mxch-card-header">
492 <h3 class="mxch-card-title">
493 <svg class="mxch-card-title-icon" 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"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
494 <?php esc_html_e('Privacy & Compliance', 'mxchat'); ?>
495 </h3>
496 </div>
497 <div class="mxch-card-body mxchat-autosave-section">
498 <?php
499 // Privacy Notice
500 mxchat_render_field_wrapper('privacy_toggle', __('Privacy Notice', 'mxchat'), function() use ($admin_instance) {
501 $admin_instance->mxchat_privacy_toggle_callback();
502 }, __('Show privacy notice below the chat widget.', 'mxchat'));
503
504 // Complianz
505 mxchat_render_field_wrapper('complianz_toggle', __('Complianz Integration', 'mxchat'), function() use ($admin_instance) {
506 $admin_instance->mxchat_complianz_toggle_callback();
507 }, __('Apply Complianz consent logic to the chatbot (requires Complianz plugin).', 'mxchat'));
508 ?>
509 </div>
510 </div>
511 </div>
512
513 <!-- ========================================
514 CHATBOT - QUICK QUESTIONS
515 ======================================== -->
516 <div id="chatbot-quick-questions" class="mxch-section">
517 <div class="mxch-content-header">
518 <h1 class="mxch-content-title"><?php esc_html_e('Quick Questions', 'mxchat'); ?></h1>
519 <p class="mxch-content-subtitle"><?php esc_html_e('Pre-defined questions displayed above the chat input to help users get started.', 'mxchat'); ?></p>
520 </div>
521
522 <div class="mxch-card">
523 <div class="mxch-card-body mxchat-autosave-section">
524 <?php
525 // Quick Question 1
526 mxchat_render_field_wrapper('popular_question_1', __('Quick Question 1', 'mxchat'), function() use ($admin_instance) {
527 $admin_instance->mxchat_popular_question_1_callback();
528 });
529
530 // Quick Question 2
531 mxchat_render_field_wrapper('popular_question_2', __('Quick Question 2', 'mxchat'), function() use ($admin_instance) {
532 $admin_instance->mxchat_popular_question_2_callback();
533 });
534
535 // Quick Question 3
536 mxchat_render_field_wrapper('popular_question_3', __('Quick Question 3', 'mxchat'), function() use ($admin_instance) {
537 $admin_instance->mxchat_popular_question_3_callback();
538 });
539
540 // Additional Questions
541 mxchat_render_field_wrapper('additional_questions', __('Additional Questions', 'mxchat'), function() use ($admin_instance) {
542 $admin_instance->mxchat_additional_popular_questions_callback();
543 }, __('Add as many quick questions as you need.', 'mxchat'));
544 ?>
545 </div>
546 </div>
547 </div>
548
549 <!-- ========================================
550 CHATBOT - RATE LIMITS
551 ======================================== -->
552 <div id="chatbot-rate-limits" class="mxch-section">
553 <div class="mxch-content-header">
554 <h1 class="mxch-content-title"><?php esc_html_e('Rate Limits', 'mxchat'); ?></h1>
555 <p class="mxch-content-subtitle"><?php esc_html_e('Set message limits for each user role to manage API usage.', 'mxchat'); ?></p>
556 </div>
557
558 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
559 <svg class="mxch-notice-icon" 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"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
560 <div>
561 <strong><?php esc_html_e('Placeholders available:', 'mxchat'); ?></strong>
562 <code>{limit}</code>, <code>{timeframe}</code>, <code>{count}</code>, <code>{remaining}</code>
563 <br>
564 <strong><?php esc_html_e('Markdown links supported:', 'mxchat'); ?></strong>
565 <code>[Link text](https://example.com)</code>
566 </div>
567 </div>
568
569 <div class="mxch-card">
570 <div class="mxch-card-body mxchat-autosave-section">
571 <?php
572 // Render rate limits with accordion
573 mxchat_render_rate_limits_accordion($admin_instance);
574 ?>
575 </div>
576 </div>
577 </div>
578
579 <!-- ========================================
580 API KEYS
581 ======================================== -->
582 <div id="api-keys" class="mxch-section">
583 <div class="mxch-content-header">
584 <h1 class="mxch-content-title"><?php esc_html_e('API Keys', 'mxchat'); ?></h1>
585 <p class="mxch-content-subtitle"><?php esc_html_e('Manage API keys for AI providers and integrations.', 'mxchat'); ?></p>
586 </div>
587
588 <div class="mxch-card">
589 <div class="mxch-card-header">
590 <h3 class="mxch-card-title">
591 <svg class="mxch-card-title-icon" 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"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
592 <?php esc_html_e('AI Providers', 'mxchat'); ?>
593 </h3>
594 </div>
595 <div class="mxch-card-body mxchat-autosave-section">
596 <table class="form-table">
597 <?php do_settings_fields('mxchat-api-keys', 'mxchat_api_keys_section'); ?>
598 </table>
599 </div>
600 </div>
601 </div>
602
603 <!-- ========================================
604 OPTIMIZATION
605 ======================================== -->
606 <div id="optimization" class="mxch-section">
607 <div class="mxch-content-header">
608 <h1 class="mxch-content-title"><?php esc_html_e('Optimization & Diagnostics', 'mxchat'); ?></h1>
609 <p class="mxch-content-subtitle"><?php esc_html_e('Optimize performance, debug issues, and manage plugin settings.', 'mxchat'); ?></p>
610 </div>
611
612 <!-- Script Loading Card -->
613 <div class="mxch-card">
614 <div class="mxch-card-header">
615 <h3 class="mxch-card-title">
616 <svg class="mxch-card-title-icon" 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"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
617 <?php esc_html_e('Script Loading', 'mxchat'); ?>
618 </h3>
619 </div>
620 <div class="mxch-card-body mxchat-autosave-section">
621 <?php
622 $script_loading_strategy = isset($options['script_loading_strategy']) ? $options['script_loading_strategy'] : 'default';
623 ?>
624 <div class="mxch-field">
625 <label class="mxch-field-label"><?php esc_html_e('Script Loading Strategy', 'mxchat'); ?></label>
626 <div class="mxch-field-control">
627 <select name="mxchat_options[script_loading_strategy]" id="script_loading_strategy" class="mxch-select mxchat-autosave-field">
628 <option value="default" <?php selected($script_loading_strategy, 'default'); ?>><?php esc_html_e('Default (Immediate)', 'mxchat'); ?></option>
629 <option value="defer" <?php selected($script_loading_strategy, 'defer'); ?>><?php esc_html_e('Deferred', 'mxchat'); ?></option>
630 <option value="delay_1s" <?php selected($script_loading_strategy, 'delay_1s'); ?>><?php esc_html_e('Delay 1 Second', 'mxchat'); ?></option>
631 <option value="delay_3s" <?php selected($script_loading_strategy, 'delay_3s'); ?>><?php esc_html_e('Delay 3 Seconds', 'mxchat'); ?></option>
632 <option value="delay_5s" <?php selected($script_loading_strategy, 'delay_5s'); ?>><?php esc_html_e('Delay 5 Seconds', 'mxchat'); ?></option>
633 <option value="on_interaction" <?php selected($script_loading_strategy, 'on_interaction'); ?>><?php esc_html_e('On User Interaction', 'mxchat'); ?></option>
634 </select>
635 </div>
636 <p class="mxch-field-description"><?php esc_html_e('Control when the chatbot script loads to improve page performance.', 'mxchat'); ?></p>
637 </div>
638
639 <?php
640 // Smart asset loading (plan-915355). Standalone option — never part
641 // of mxchat_options, so mxchat_sanitize() can't strip it.
642 $smart_asset_loading = get_option('mxchat_smart_asset_loading', 'off');
643 ?>
644 <div class="mxch-field" style="margin-top: 16px;">
645 <label class="mxch-toggle">
646 <input type="checkbox" class="mxch-toggle-input mxchat-autosave-field" id="mxchat_smart_asset_loading" name="mxchat_smart_asset_loading" value="on" <?php checked($smart_asset_loading, 'on'); ?>>
647 <span class="mxch-toggle-switch"></span>
648 <span class="mxch-toggle-label"><?php esc_html_e('Only load chatbot files on pages where the chatbot appears', 'mxchat'); ?></span>
649 </label>
650 <p class="mxch-field-description"><?php esc_html_e('Skips the chatbot\'s CSS and JavaScript on pages where the widget is hidden (per-page visibility, post type include/exclude, or auto-display disabled). Pages using the chatbot shortcode still load everything automatically. Helps with "reduce unused CSS/JS" audit findings.', 'mxchat'); ?></p>
651 <p class="mxch-field-hint"><?php esc_html_e('If your theme or page builder inserts the chatbot in an unusual way and it stops appearing, turn this off — or force-load assets with the mxchat_should_load_assets filter.', 'mxchat'); ?></p>
652 </div>
653
654 <div class="mxch-notice mxch-notice-info" style="margin-top: 16px;">
655 <svg class="mxch-notice-icon" 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"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
656 <div>
657 <strong><?php esc_html_e('Loading Strategies:', 'mxchat'); ?></strong>
658 <ul style="margin: 8px 0 0 0; padding-left: 20px;">
659 <li><strong><?php esc_html_e('Default:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads immediately in the footer. Best for sites where chat is critical.', 'mxchat'); ?></li>
660 <li><strong><?php esc_html_e('Deferred:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads after HTML parsing completes. Good balance of performance and availability.', 'mxchat'); ?></li>
661 <li><strong><?php esc_html_e('Delay:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads after a set delay. Helps improve LCP scores.', 'mxchat'); ?></li>
662 <li><strong><?php esc_html_e('On User Interaction:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads when user scrolls, moves mouse, or touches screen. Best for Core Web Vitals but chat appears with slight delay.', 'mxchat'); ?></li>
663 </ul>
664 </div>
665 </div>
666 </div>
667 </div>
668
669 <!-- Page Cache Compatibility Card -->
670 <div class="mxch-card">
671 <div class="mxch-card-header">
672 <h3 class="mxch-card-title">
673 <svg class="mxch-card-title-icon" 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"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
674 <?php esc_html_e('Page Cache Compatibility', 'mxchat'); ?>
675 </h3>
676 </div>
677 <div class="mxch-card-body">
678 <p class="mxch-field-description" style="margin-top: 0;"><?php esc_html_e('MxChat\'s chat send (and stream send, plus PDF / Word upload) all POST to /wp-admin/admin-ajax.php. Page-cache plugins occasionally cache those responses, which breaks the per-session nonce flow on the first message. MxChat auto-tells the five most common cache plugins to never cache chat AJAX via their own filter APIs — no setup needed for those. For other cache plugins (or Cloudflare APO), copy the manual exclusion string into your cache plugin\'s exclusion list.', 'mxchat'); ?></p>
679
680 <?php
681 $mxch_cache_rows = array(
682 array('name' => 'WP Rocket', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax\\.php\\?action=mxchat_.*'),
683 array('name' => 'LiteSpeed Cache', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax.php?action=mxchat_*'),
684 array('name' => 'W3 Total Cache', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax.php?action=mxchat_*'),
685 array('name' => 'FlyingPress', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax.php?action=mxchat_*'),
686 array('name' => 'WP Super Cache', 'state' => 'auto', 'exclusion' => 'wp-admin/admin-ajax.php'),
687 array('name' => 'Cloudflare APO', 'state' => 'manual', 'exclusion' => 'Page Rule / Cache Rule: URI Path contains "/wp-admin/admin-ajax.php" → Bypass Cache'),
688 );
689 ?>
690
691 <div class="mxch-cache-compat-grid" style="display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 16px;">
692 <?php foreach ($mxch_cache_rows as $row) :
693 $is_auto = ($row['state'] === 'auto');
694 $pill_class = $is_auto ? 'mxch-status-pill-active' : 'mxch-status-pill-warning';
695 $pill_label = $is_auto ? __('Auto-handled', 'mxchat') : __('Manual', 'mxchat');
696 ?>
697 <div class="mxch-cache-compat-row" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 14px; background: #f8fafc; border: 1px solid var(--mxch-card-border); border-radius: var(--mxch-radius-md);">
698 <div style="display: flex; align-items: center; gap: 10px; min-width: 0; flex: 0 0 auto;">
699 <span style="font-weight: 500;"><?php echo esc_html($row['name']); ?></span>
700 <span class="mxch-status-pill <?php echo esc_attr($pill_class); ?>">
701 <span class="mxch-status-dot"></span>
702 <?php echo esc_html($pill_label); ?>
703 </span>
704 </div>
705 <div style="display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0;">
706 <code style="background: #fff; border: 1px solid var(--mxch-card-border); padding: 4px 8px; border-radius: var(--mxch-radius-sm); font-size: 12px; color: var(--mxch-text-secondary); flex: 1 1 auto; overflow-x: auto; white-space: nowrap; min-width: 0;"><?php echo esc_html($row['exclusion']); ?></code>
707 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" data-mxch-copy="<?php echo esc_attr($row['exclusion']); ?>" style="flex: 0 0 auto;">
708 <?php esc_html_e('Copy', 'mxchat'); ?>
709 </button>
710 </div>
711 </div>
712 <?php endforeach; ?>
713 </div>
714
715 <div class="mxch-notice mxch-notice-info" style="margin-top: 16px;">
716 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
717 <span><?php esc_html_e('Auto-handled plugins receive a filter from MxChat that excludes chat AJAX from their cache. Even so, having the exclusion in your own exclusion list is harmless and acts as a belt-and-suspenders guarantee.', 'mxchat'); ?></span>
718 </div>
719 </div>
720 </div>
721
722 <!-- Provider Reliability Card -->
723 <?php
724 $auto_retry_enabled = !isset($options['auto_retry_on_transient_error']) ||
725 (string) $options['auto_retry_on_transient_error'] !== '0';
726 ?>
727 <div class="mxch-card">
728 <div class="mxch-card-header">
729 <h3 class="mxch-card-title">
730 <svg class="mxch-card-title-icon" 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"><path d="M21 12a9 9 0 1 1-6.219-8.56"/><polyline points="21 4 21 10 15 10"/></svg>
731 <?php esc_html_e('Provider Reliability', 'mxchat'); ?>
732 </h3>
733 <span class="mxch-status-pill <?php echo $auto_retry_enabled ? 'mxch-status-pill-active' : 'mxch-status-pill-disabled'; ?>">
734 <span class="mxch-status-dot"></span>
735 <?php echo $auto_retry_enabled ? esc_html__('Active', 'mxchat') : esc_html__('Disabled', 'mxchat'); ?>
736 </span>
737 </div>
738 <div class="mxch-card-body mxchat-autosave-section">
739 <div class="mxch-field">
740 <label class="mxch-field-label" for="auto_retry_on_transient_error">
741 <?php esc_html_e('Auto-retry on provider overload', 'mxchat'); ?>
742 </label>
743 <div class="mxch-field-control">
744 <label class="mxch-toggle">
745 <input type="checkbox"
746 id="auto_retry_on_transient_error"
747 name="mxchat_options[auto_retry_on_transient_error]"
748 class="mxchat-autosave-field"
749 value="1"
750 <?php checked($auto_retry_enabled); ?> />
751 <span class="mxch-toggle-slider"></span>
752 </label>
753 </div>
754 <p class="mxch-field-description"><?php esc_html_e('When the AI provider returns a transient error (rate-limit / overload / 429 / 503 / Gemini "model overloaded"), MxChat retries the chat-send up to twice before surfacing the error. Visitors see a brief pause instead of a raw provider error in the bot bubble.', 'mxchat'); ?></p>
755 <p class="mxch-field-hint"><?php esc_html_e('Configuration errors (invalid API key, 401/403/404/422) still surface immediately — never retried.', 'mxchat'); ?></p>
756 </div>
757 </div>
758 </div>
759
760 <!-- Debug Mode Card -->
761 <?php
762 $debug_mode = isset($options['debug_mode']) ? $options['debug_mode'] : 'off';
763 $debug_active = ($debug_mode === 'on');
764 ?>
765 <div class="mxch-card">
766 <div class="mxch-card-header">
767 <h3 class="mxch-card-title">
768 <svg class="mxch-card-title-icon" 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"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/><path d="m9 12 2 2 4-4"/></svg>
769 <?php esc_html_e('Debug Mode', 'mxchat'); ?>
770 </h3>
771 </div>
772 <div class="mxch-card-body">
773 <div class="mxch-field">
774 <label class="mxch-toggle">
775 <input type="checkbox" class="mxch-toggle-input" id="mxchat_debug_mode" name="mxchat_options[debug_mode]" value="on" <?php checked($debug_active); ?>>
776 <span class="mxch-toggle-switch"></span>
777 <span class="mxch-toggle-label"><?php esc_html_e('Enable Debug Logging', 'mxchat'); ?></span>
778 </label>
779 <p class="mxch-field-description"><?php esc_html_e('Tracks settings changes, knowledge base errors, API issues, and embedding failures. Enable only when troubleshooting. Max 100 entries stored.', 'mxchat'); ?></p>
780 </div>
781 </div>
782 </div>
783
784 <!-- Debug Log Viewer Card -->
785 <div class="mxch-card">
786 <div class="mxch-card-header" style="display: flex; justify-content: space-between; align-items: center;">
787 <h3 class="mxch-card-title">
788 <svg class="mxch-card-title-icon" 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"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
789 <?php esc_html_e('Debug Log', 'mxchat'); ?>
790 </h3>
791 <div style="display: flex; gap: 8px; align-items: center;">
792 <span id="mxchat-log-count" class="mxch-badge" style="display: none;"></span>
793 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" id="mxchat-refresh-log">
794 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"/></svg>
795 <?php esc_html_e('Refresh', 'mxchat'); ?>
796 </button>
797 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" id="mxchat-clear-log">
798 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
799 <?php esc_html_e('Clear', 'mxchat'); ?>
800 </button>
801 </div>
802 </div>
803 <div class="mxch-card-body">
804 <div id="mxchat-debug-log" class="mxchat-debug-log-viewer">
805 <div class="mxchat-debug-log-empty">
806 <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" style="opacity: 0.3;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
807 <p><?php esc_html_e('No log entries yet. Enable debug mode to start logging.', 'mxchat'); ?></p>
808 </div>
809 </div>
810 </div>
811 </div>
812
813 <!-- Settings Tools Card -->
814 <div class="mxch-card">
815 <div class="mxch-card-header">
816 <h3 class="mxch-card-title">
817 <svg class="mxch-card-title-icon" 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"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
818 <?php esc_html_e('Settings Tools', 'mxchat'); ?>
819 </h3>
820 </div>
821 <div class="mxch-card-body">
822 <div class="mxch-tools-grid">
823 <!-- Export Settings -->
824 <div class="mxch-tool-item">
825 <div class="mxch-tool-icon">
826 <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"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
827 </div>
828 <div class="mxch-tool-content">
829 <h4><?php esc_html_e('Export Settings', 'mxchat'); ?></h4>
830 <p><?php esc_html_e('Download a JSON file of your current settings for backup or support. API keys are masked for security.', 'mxchat'); ?></p>
831 <button type="button" class="mxch-btn mxch-btn-secondary" id="mxchat-export-settings">
832 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
833 <?php esc_html_e('Export Settings', 'mxchat'); ?>
834 </button>
835 </div>
836 </div>
837
838 <!-- Reset All Settings -->
839 <div class="mxch-tool-item mxch-tool-item-danger">
840 <div class="mxch-tool-icon">
841 <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"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
842 </div>
843 <div class="mxch-tool-content">
844 <h4><?php esc_html_e('Reset All Settings', 'mxchat'); ?></h4>
845 <p><?php esc_html_e('Reset all MxChat settings to their default values. This action cannot be undone.', 'mxchat'); ?></p>
846 <button type="button" class="mxch-btn mxch-btn-danger" id="mxchat-reset-settings">
847 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/></svg>
848 <?php esc_html_e('Reset All Settings', 'mxchat'); ?>
849 </button>
850 </div>
851 </div>
852 </div>
853 </div>
854 </div>
855
856 <!-- Reset Confirmation Modal -->
857 <div id="mxchat-reset-modal" class="mxch-modal" style="display: none;">
858 <div class="mxch-modal-backdrop"></div>
859 <div class="mxch-modal-content">
860 <div class="mxch-modal-header">
861 <h3><?php esc_html_e('Reset All Settings', 'mxchat'); ?></h3>
862 <button type="button" class="mxch-modal-close" id="mxchat-reset-modal-close">&times;</button>
863 </div>
864 <div class="mxch-modal-body">
865 <div class="mxch-notice mxch-notice-warning">
866 <svg class="mxch-notice-icon" 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"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
867 <div>
868 <strong><?php esc_html_e('Warning: This action cannot be undone!', 'mxchat'); ?></strong>
869 <p><?php esc_html_e('All your MxChat settings including API keys, customizations, and configurations will be permanently deleted.', 'mxchat'); ?></p>
870 </div>
871 </div>
872 <div class="mxch-field" style="margin-top: 20px;">
873 <label class="mxch-field-label"><?php esc_html_e('Type RESET to confirm:', 'mxchat'); ?></label>
874 <input type="text" id="mxchat-reset-confirmation" class="mxch-input" placeholder="RESET" autocomplete="off">
875 </div>
876 </div>
877 <div class="mxch-modal-footer">
878 <button type="button" class="mxch-btn mxch-btn-secondary" id="mxchat-reset-cancel"><?php esc_html_e('Cancel', 'mxchat'); ?></button>
879 <button type="button" class="mxch-btn mxch-btn-danger" id="mxchat-reset-confirm" disabled><?php esc_html_e('Reset All Settings', 'mxchat'); ?></button>
880 </div>
881 </div>
882 </div>
883 </div>
884
885 <!-- ========================================
886 INTEGRATIONS - TOOLBAR
887 ======================================== -->
888 <div id="integrations-toolbar" class="mxch-section">
889 <div class="mxch-content-header">
890 <h1 class="mxch-content-title"><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h1>
891 <p class="mxch-content-subtitle"><?php esc_html_e('Configure the chat toolbar and document upload features.', 'mxchat'); ?></p>
892 </div>
893
894 <div class="mxch-card">
895 <div class="mxch-card-body mxchat-autosave-section">
896 <table class="form-table">
897 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
898 </table>
899 </div>
900 </div>
901 </div>
902
903 <!-- ========================================
904 INTEGRATIONS - LOOPS
905 ======================================== -->
906 <div id="integrations-loops" class="mxch-section">
907 <div class="mxch-content-header">
908 <h1 class="mxch-content-title"><?php esc_html_e('Loops Integration', 'mxchat'); ?></h1>
909 <p class="mxch-content-subtitle"><?php esc_html_e('Configure email capture with Loops for mailing list growth.', 'mxchat'); ?></p>
910 </div>
911
912 <div class="mxch-card">
913 <div class="mxch-card-body mxchat-autosave-section">
914 <table class="form-table">
915 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
916 </table>
917 </div>
918 </div>
919 </div>
920
921 <!-- ========================================
922 INTEGRATIONS - BRAVE SEARCH
923 ======================================== -->
924 <div id="integrations-brave" class="mxch-section">
925 <div class="mxch-content-header">
926 <h1 class="mxch-content-title"><?php esc_html_e('Brave Search', 'mxchat'); ?></h1>
927 <p class="mxch-content-subtitle"><?php esc_html_e('Configure Brave Search API for web search capabilities.', 'mxchat'); ?></p>
928 </div>
929
930 <div class="mxch-card">
931 <div class="mxch-card-body mxchat-autosave-section">
932 <table class="form-table">
933 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
934 </table>
935 </div>
936 </div>
937 </div>
938
939 <!-- ========================================
940 INTEGRATIONS - SLACK
941 ======================================== -->
942 <div id="integrations-slack" class="mxch-section">
943 <div class="mxch-content-header">
944 <h1 class="mxch-content-title"><?php esc_html_e('Slack', 'mxchat'); ?></h1>
945 <p class="mxch-content-subtitle"><?php esc_html_e('Connect your chatbot to Slack for live human support.', 'mxchat'); ?></p>
946 </div>
947
948 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
949 <svg class="mxch-notice-icon" 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"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
950 <div>
951 <?php printf(
952 esc_html__('Visit our %s for setup instructions.', 'mxchat'),
953 '<a href="https://mxchat.ai/documentation/mxchat-core#slack-live-agent-handoff" target="_blank">' . esc_html__('documentation page', 'mxchat') . '</a>'
954 ); ?>
955 </div>
956 </div>
957
958 <div class="mxch-card">
959 <div class="mxch-card-body mxchat-autosave-section">
960 <table class="form-table">
961 <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
962 </table>
963 </div>
964 </div>
965
966 <div class="mxch-card" style="margin-top: 24px;">
967 <div class="mxch-card-header">
968 <h3 class="mxch-card-title">
969 <svg class="mxch-card-title-icon" 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"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
970 <?php esc_html_e('Test Connection', 'mxchat'); ?>
971 </h3>
972 </div>
973 <div class="mxch-card-body">
974 <p class="mxch-field-description" style="margin-bottom: 16px;">
975 <?php esc_html_e('Test your Slack bot token to verify it has the required permissions. This will check if the bot can authenticate and create channels.', 'mxchat'); ?>
976 </p>
977 <button type="button" id="mxchat-test-slack-connection" class="mxch-btn mxch-btn-secondary">
978 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
979 <?php esc_html_e('Test Slack Connection', 'mxchat'); ?>
980 </button>
981 <div id="mxchat-slack-test-result" style="margin-top: 16px; display: none;"></div>
982 </div>
983 </div>
984
985 <div class="mxch-card" style="margin-top: 24px;">
986 <div class="mxch-card-header">
987 <h3 class="mxch-card-title">
988 <svg class="mxch-card-title-icon" 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"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
989 <?php esc_html_e('Required OAuth Scopes', 'mxchat'); ?>
990 </h3>
991 </div>
992 <div class="mxch-card-body">
993 <p class="mxch-field-description" style="margin-bottom: 12px;">
994 <?php esc_html_e('Your Slack app needs these Bot Token Scopes (OAuth & Permissions > Scopes):', 'mxchat'); ?>
995 </p>
996 <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; background: #f8f9fa; padding: 16px; border-radius: 8px; font-family: monospace; font-size: 13px;">
997 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">channels:manage</code>
998 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">channels:read</code>
999 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">chat:write</code>
1000 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">chat:write.public</code>
1001 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">groups:write</code>
1002 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">users:read</code>
1003 </div>
1004 <p class="mxch-field-description" style="margin-top: 12px;">
1005 <?php esc_html_e('After adding scopes, reinstall the app to your workspace.', 'mxchat'); ?>
1006 </p>
1007 </div>
1008 </div>
1009 </div>
1010
1011 <!-- ========================================
1012 INTEGRATIONS - TELEGRAM
1013 ======================================== -->
1014 <div id="integrations-telegram" class="mxch-section">
1015 <div class="mxch-content-header">
1016 <h1 class="mxch-content-title"><?php esc_html_e('Telegram', 'mxchat'); ?></h1>
1017 <p class="mxch-content-subtitle"><?php esc_html_e('Connect your chatbot to Telegram for live human support.', 'mxchat'); ?></p>
1018 </div>
1019
1020 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
1021 <svg class="mxch-notice-icon" 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"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
1022 <div>
1023 <?php esc_html_e('Telegram Live Agent requires a Telegram bot and a supergroup with forum topics enabled.', 'mxchat'); ?>
1024 <a href="https://mxchat.ai/documentation-bot/" target="_blank" style="margin-left: 5px;"><?php esc_html_e('Chat with our advanced doc bot for assistance setting up.', 'mxchat'); ?></a>
1025 </div>
1026 </div>
1027
1028 <div class="mxch-card">
1029 <div class="mxch-card-body mxchat-autosave-section">
1030 <table class="form-table">
1031 <?php do_settings_fields('mxchat-embed', 'mxchat_telegram_section'); ?>
1032 </table>
1033 </div>
1034 </div>
1035
1036 <div class="mxch-card" style="margin-top: 24px;">
1037 <div class="mxch-card-header">
1038 <h3 class="mxch-card-title">
1039 <svg class="mxch-card-title-icon" 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"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
1040 <?php esc_html_e('Webhook Setup', 'mxchat'); ?>
1041 </h3>
1042 </div>
1043 <div class="mxch-card-body">
1044 <?php
1045 $telegram_bot_token = $options['telegram_bot_token'] ?? '';
1046 $telegram_webhook_secret = $options['telegram_webhook_secret'] ?? '';
1047 $webhook_url = rest_url('mxchat/v1/telegram-webhook');
1048 ?>
1049
1050 <p class="mxch-field-description" style="margin-bottom: 8px;">
1051 <strong><?php esc_html_e('Your Webhook URL:', 'mxchat'); ?></strong>
1052 </p>
1053 <div class="mxch-webhook-url-display" style="margin-bottom: 20px;">
1054 <code id="mxch-telegram-webhook-url"><?php echo esc_url($webhook_url); ?></code>
1055 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" onclick="navigator.clipboard.writeText(document.getElementById('mxch-telegram-webhook-url').textContent); this.textContent='Copied!'; setTimeout(() => this.textContent='Copy', 2000);">
1056 <?php esc_html_e('Copy', 'mxchat'); ?>
1057 </button>
1058 </div>
1059
1060 <?php if (!empty($telegram_bot_token)) : ?>
1061 <p class="mxch-field-description" style="margin-bottom: 8px;">
1062 <strong><?php esc_html_e('Register Webhook URL:', 'mxchat'); ?></strong><br>
1063 <?php esc_html_e('Copy and paste this URL into your browser to register the webhook with Telegram:', 'mxchat'); ?>
1064 </p>
1065 <?php
1066 $registration_url = 'https://api.telegram.org/bot' . $telegram_bot_token . '/setWebhook?url=' . urlencode($webhook_url);
1067 if (!empty($telegram_webhook_secret)) {
1068 $registration_url .= '&secret_token=' . urlencode($telegram_webhook_secret);
1069 }
1070 ?>
1071 <div class="mxch-webhook-url-display" style="margin-bottom: 12px;">
1072 <code id="mxch-telegram-registration-url" style="word-break: break-all; font-size: 12px;"><?php echo esc_url($registration_url); ?></code>
1073 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" onclick="navigator.clipboard.writeText(document.getElementById('mxch-telegram-registration-url').textContent); this.textContent='Copied!'; setTimeout(() => this.textContent='Copy', 2000);">
1074 <?php esc_html_e('Copy', 'mxchat'); ?>
1075 </button>
1076 </div>
1077 <p class="mxch-field-description" style="color: #22c55e;">
1078 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
1079 <?php esc_html_e('You should see {"ok":true,"result":true,"description":"Webhook was set"} after visiting the URL.', 'mxchat'); ?>
1080 </p>
1081 <?php else : ?>
1082 <p class="mxch-field-description" style="color: #f59e0b;">
1083 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
1084 <?php esc_html_e('Enter your Bot Token above and save to generate the registration URL.', 'mxchat'); ?>
1085 </p>
1086 <?php endif; ?>
1087 </div>
1088 </div>
1089 </div>
1090
1091 <!-- ========================================
1092 TUTORIALS
1093 ======================================== -->
1094 <div id="tutorials" class="mxch-section">
1095 <div class="mxch-content-header">
1096 <h1 class="mxch-content-title"><?php esc_html_e('Video Tutorials', 'mxchat'); ?></h1>
1097 <p class="mxch-content-subtitle"><?php esc_html_e('Learn how to get the most out of MxChat with our video guides.', 'mxchat'); ?></p>
1098 </div>
1099
1100 <?php
1101 // Setup Guide — reopen the onboarding wizard (plan-347916, moved here from Display tab).
1102 // Only shows once the user has dismissed/graduated from the Onboarding page.
1103 if (function_exists('mxchat_onboarding_is_dismissed') && mxchat_onboarding_is_dismissed()):
1104 $auto_grad = function_exists('mxchat_onboarding_is_auto_graduated') && mxchat_onboarding_is_auto_graduated();
1105 ?>
1106 <div class="mxch-card mxch-onboarding-unhide-card" style="margin-bottom: 24px;">
1107 <div class="mxch-card-header">
1108 <h3 class="mxch-card-title">
1109 <svg class="mxch-card-title-icon" 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"><path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c0 2 3 3 6 3s6-1 6-3v-5"/></svg>
1110 <?php esc_html_e('Setup Guide', 'mxchat'); ?>
1111 </h3>
1112 </div>
1113 <div class="mxch-card-body">
1114 <p style="margin:0 0 12px;color:var(--mxch-text-secondary);font-size:13px;line-height:1.5;">
1115 <?php if ($auto_grad): ?>
1116 <?php esc_html_e('You finished the setup guide. Reopen it any time to revisit the steps or change a setup choice.', 'mxchat'); ?>
1117 <?php else: ?>
1118 <?php esc_html_e('You hid the setup guide from the MxChat menu. Reopen it any time to revisit the steps.', 'mxchat'); ?>
1119 <?php endif; ?>
1120 </p>
1121 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="margin:0;">
1122 <?php wp_nonce_field('mxchat_unhide_onboarding'); ?>
1123 <input type="hidden" name="action" value="mxchat_unhide_onboarding" />
1124 <button type="submit" class="mxch-btn mxch-btn-secondary mxch-btn-sm">
1125 <?php esc_html_e('Reopen setup guide', 'mxchat'); ?>
1126 </button>
1127 </form>
1128 </div>
1129 </div>
1130 <?php endif; ?>
1131
1132 <div class="mxch-card" style="margin-bottom: 24px;">
1133 <div class="mxch-card-body">
1134 <div style="display: flex; align-items: flex-start; gap: 16px;">
1135 <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" style="color: var(--mxch-warning); flex-shrink: 0;"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
1136 <div>
1137 <h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 600;"><?php esc_html_e('Need Help?', 'mxchat'); ?></h3>
1138 <p style="margin: 0 0 12px 0; color: var(--mxch-text-secondary);"><?php esc_html_e('If you\'re having trouble or believe something is not working as expected, please submit a support ticket.', 'mxchat'); ?></p>
1139 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" class="mxch-btn mxch-btn-secondary mxch-btn-sm">
1140 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/><path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/></svg>
1141 <?php esc_html_e('Submit Support Ticket', 'mxchat'); ?>
1142 </a>
1143 </div>
1144 </div>
1145 </div>
1146 </div>
1147
1148 <div class="mxch-tutorials-grid">
1149 <?php
1150 $tutorials = array(
1151 array(
1152 'title' => __('Quick Setup with MxChat', 'mxchat'),
1153 'description' => __('Learn how to quickly setup and understand how your chatbot works.', 'mxchat'),
1154 'url' => 'https://www.youtube.com/watch?v=3BqoiyWaQiM&t'
1155 ),
1156 array(
1157 'title' => __('AI Content Generation', 'mxchat'),
1158 'description' => __('Generate full blog posts and landing pages with AI—from prompt to publish in minutes.', 'mxchat'),
1159 'url' => 'https://www.youtube.com/watch?v=n8TeEeHpxs4'
1160 ),
1161 array(
1162 'title' => __('Chat With Your GSC Data', 'mxchat'),
1163 'description' => __('Find content gaps, quick win keywords, and SEO opportunities by chatting with your Google Search Console data using the Admin Assistant add-on.', 'mxchat'),
1164 'url' => 'https://www.youtube.com/watch?v=ytgMou_7SZA&t=2s'
1165 ),
1166 array(
1167 'title' => __('AI Theme Generator', 'mxchat'),
1168 'description' => __('Learn how to instantly restyle your chatbot using plain English prompts with real-time previews.', 'mxchat'),
1169 'url' => 'https://www.youtube.com/watch?v=rSQDW2qbtRU&t'
1170 ),
1171 array(
1172 'title' => __('MxChat Forms', 'mxchat'),
1173 'description' => __('Create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'),
1174 'url' => 'https://www.youtube.com/watch?v=3MrWy5dRalA'
1175 ),
1176 array(
1177 'title' => __('Admin Assistant Add-on', 'mxchat'),
1178 'description' => __('Bring a ChatGPT-like experience directly inside your WordPress dashboard.', 'mxchat'),
1179 'url' => 'https://youtu.be/AdEA1k-UCFM'
1180 ),
1181 array(
1182 'title' => __('Chat Themes', 'mxchat'),
1183 'description' => __('Customize appearance with real-time previews to match your brand.', 'mxchat'),
1184 'url' => 'https://youtu.be/MfbB9mZi6ag'
1185 ),
1186 array(
1187 'title' => __('WooCommerce Integration', 'mxchat'),
1188 'description' => __('Provide product recommendations and shopping assistance to customers.', 'mxchat'),
1189 'url' => 'https://www.youtube.com/watch?v=WsqAppHRGdA'
1190 ),
1191 array(
1192 'title' => __('Knowledge Base Setup', 'mxchat'),
1193 'description' => __('Set up your knowledge base using PDFs, sitemaps, and manual entries.', 'mxchat'),
1194 'url' => 'https://www.youtube.com/watch?v=8Ztjs66-VTo'
1195 ),
1196 array(
1197 'title' => __('Document Chat', 'mxchat'),
1198 'description' => __('Chat with PDF and Word documents for enhanced document analysis.', 'mxchat'),
1199 'url' => 'https://www.youtube.com/watch?v=j_c45WWCTG0'
1200 ),
1201 array(
1202 'title' => __('Perplexity Integration', 'mxchat'),
1203 'description' => __('Enable real-time web search capabilities for your chatbot.', 'mxchat'),
1204 'url' => 'https://youtu.be/wpKkbt24-bo'
1205 ),
1206 array(
1207 'title' => __('Brave Search Intent', 'mxchat'),
1208 'description' => __('Leverage Brave Search for improved query understanding.', 'mxchat'),
1209 'url' => 'https://www.youtube.com/watch?v=7vDL5H7vToc'
1210 ),
1211 array(
1212 'title' => __('Loops Email Capture', 'mxchat'),
1213 'description' => __('Set up email capture with Loops to grow your mailing list.', 'mxchat'),
1214 'url' => 'https://www.youtube.com/watch?v=CNgm5TYDyTc'
1215 ),
1216 array(
1217 'title' => __('AI Agent Testing', 'mxchat'),
1218 'description' => __('Evaluate and improve your chatbot\'s performance and accuracy.', 'mxchat'),
1219 'url' => 'https://www.youtube.com/watch?v=A0jowbpyX54'
1220 ),
1221 );
1222
1223 foreach ($tutorials as $tutorial): ?>
1224 <div class="mxch-tutorial-card">
1225 <div class="mxch-tutorial-content">
1226 <h3 class="mxch-tutorial-title"><?php echo esc_html($tutorial['title']); ?></h3>
1227 <p class="mxch-tutorial-description"><?php echo esc_html($tutorial['description']); ?></p>
1228 <a href="<?php echo esc_url($tutorial['url']); ?>" target="_blank" rel="noopener" class="mxch-tutorial-link">
1229 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
1230 <?php esc_html_e('Watch Tutorial', 'mxchat'); ?>
1231 </a>
1232 </div>
1233 </div>
1234 <?php endforeach; ?>
1235 </div>
1236 </div>
1237
1238 <!-- =====================================================
1239 TESTING SECTION
1240 ===================================================== -->
1241 <div id="testing" class="mxch-section">
1242 <div class="mxch-content-header">
1243 <h1 class="mxch-content-title"><?php esc_html_e('Testing', 'mxchat'); ?></h1>
1244 <p class="mxch-content-subtitle"><?php esc_html_e('Test your chatbot and inspect debug data in real time.', 'mxchat'); ?></p>
1245 </div>
1246
1247 <div class="mxch-testing-layout">
1248 <!-- Left Column: Debug Panel -->
1249 <div class="mxch-testing-debug-panel">
1250 <!-- Quick Actions -->
1251 <div class="mxch-card">
1252 <div class="mxch-card-body">
1253 <h3 class="mxch-testing-section-title"><?php esc_html_e('Quick Actions', 'mxchat'); ?></h3>
1254 <div class="mxch-testing-actions-row">
1255 <button type="button" class="mxch-testing-btn mxch-testing-btn-danger" id="mxch-testing-clear-session">
1256 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
1257 <?php esc_html_e('Clear Chat Session', 'mxchat'); ?>
1258 </button>
1259 <button type="button" class="mxch-testing-btn mxch-testing-btn-secondary" id="mxch-testing-clear-debug">
1260 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M2 12h20"/></svg>
1261 <?php esc_html_e('Clear Debug Log', 'mxchat'); ?>
1262 </button>
1263 </div>
1264 </div>
1265 </div>
1266
1267 <!-- Last Query Analysis -->
1268 <div class="mxch-card">
1269 <div class="mxch-card-body">
1270 <h3 class="mxch-testing-section-title"><?php esc_html_e('Last Query Analysis', 'mxchat'); ?></h3>
1271
1272 <div class="mxch-testing-field">
1273 <label><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></label>
1274 <span id="mxch-testing-threshold"><?php esc_html_e('Loading...', 'mxchat'); ?></span>
1275 </div>
1276
1277 <div class="mxch-testing-field">
1278 <label><?php esc_html_e('User Query', 'mxchat'); ?></label>
1279 <div id="mxch-testing-last-query" class="mxch-testing-query-display"><?php esc_html_e('Waiting for next query...', 'mxchat'); ?></div>
1280 </div>
1281
1282 <div class="mxch-testing-field">
1283 <label><?php esc_html_e('Approved URLs for Citations', 'mxchat'); ?></label>
1284 <div id="mxch-testing-approved-urls" class="mxch-testing-results">
1285 <div class="mxch-testing-no-data"><?php esc_html_e('No URL data yet', 'mxchat'); ?></div>
1286 </div>
1287 </div>
1288
1289 <div class="mxch-testing-field">
1290 <label><?php esc_html_e('Document Matches', 'mxchat'); ?></label>
1291 <div id="mxch-testing-similarity-scores" class="mxch-testing-results">
1292 <div class="mxch-testing-no-data"><?php esc_html_e('No query data yet', 'mxchat'); ?></div>
1293 </div>
1294 </div>
1295
1296 <div class="mxch-testing-field">
1297 <label><?php esc_html_e('Actions Triggered', 'mxchat'); ?></label>
1298 <div id="mxch-testing-action-scores" class="mxch-testing-results">
1299 <div class="mxch-testing-no-data"><?php esc_html_e('No action data yet', 'mxchat'); ?></div>
1300 </div>
1301 </div>
1302 </div>
1303 </div>
1304
1305 <!-- System Information -->
1306 <div class="mxch-card">
1307 <div class="mxch-card-body">
1308 <h3 class="mxch-testing-section-title"><?php esc_html_e('System Information', 'mxchat'); ?></h3>
1309
1310 <div class="mxch-testing-field">
1311 <label><?php esc_html_e('System Prompt', 'mxchat'); ?></label>
1312 <div id="mxch-testing-system-prompt" class="mxch-testing-system-prompt"><?php esc_html_e('Loading...', 'mxchat'); ?></div>
1313 </div>
1314
1315 <div class="mxch-testing-field">
1316 <label><?php esc_html_e('Knowledge Base', 'mxchat'); ?></label>
1317 <span id="mxch-testing-kb-status"><?php esc_html_e('Checking...', 'mxchat'); ?></span>
1318 </div>
1319 </div>
1320 </div>
1321
1322 <!-- Debug Log -->
1323 <div class="mxch-card">
1324 <div class="mxch-card-body">
1325 <h3 class="mxch-testing-section-title"><?php esc_html_e('Debug Log', 'mxchat'); ?></h3>
1326 <div id="mxch-testing-debug-console" class="mxch-testing-debug-console">
1327 <div class="mxch-testing-debug-entry"><?php esc_html_e('Debug panel ready - send a message to begin...', 'mxchat'); ?></div>
1328 </div>
1329 </div>
1330 </div>
1331 </div>
1332
1333 <!-- Right Column: Chatbot Replica -->
1334 <div class="mxch-testing-chatbot-column">
1335 <div class="mxch-testing-chatbot-scope">
1336 <?php
1337 // Render the chatbot inline for testing
1338 echo do_shortcode('[mxchat_chatbot floating="no" bot_id="testing"]');
1339 ?>
1340 </div>
1341 </div>
1342 </div>
1343 </div>
1344
1345 </main>
1346 </div>
1347
1348 <script>
1349 document.addEventListener('DOMContentLoaded', function() {
1350 // Sidebar navigation
1351 const navLinks = document.querySelectorAll('.mxch-nav-link, .mxch-nav-sub-link');
1352 const sections = document.querySelectorAll('.mxch-section');
1353 const navItems = document.querySelectorAll('.mxch-nav-item');
1354
1355 function showSection(target) {
1356 // Show target section
1357 sections.forEach(section => {
1358 section.classList.remove('active');
1359 if (section.id === target) {
1360 section.classList.add('active');
1361 }
1362 });
1363
1364 // Scroll content area to top
1365 const contentArea = document.querySelector('.mxch-content');
1366 if (contentArea) {
1367 contentArea.scrollTop = 0;
1368 }
1369 }
1370
1371 function setActiveNav(clickedLink) {
1372 // Remove active from all links
1373 navLinks.forEach(l => l.classList.remove('active'));
1374
1375 // Add active to clicked link
1376 clickedLink.classList.add('active');
1377
1378 // If clicking a sub-link, also highlight parent
1379 if (clickedLink.classList.contains('mxch-nav-sub-link')) {
1380 const parent = clickedLink.closest('.mxch-nav-item');
1381 if (parent) {
1382 parent.querySelector('.mxch-nav-link').classList.add('active');
1383 }
1384 }
1385 }
1386
1387 navLinks.forEach(link => {
1388 link.addEventListener('click', function(e) {
1389 e.preventDefault();
1390 e.stopPropagation();
1391 const target = this.dataset.target;
1392 const parentItem = this.closest('.mxch-nav-item');
1393 const hasSubmenu = parentItem && parentItem.querySelector('.mxch-nav-sub');
1394
1395 // If this is a parent nav link WITH children (expandable menu)
1396 if (this.classList.contains('mxch-nav-link') && hasSubmenu) {
1397 const wasExpanded = parentItem.classList.contains('expanded');
1398
1399 // Collapse all other expandable items
1400 navItems.forEach(item => {
1401 if (item !== parentItem && item.querySelector('.mxch-nav-sub')) {
1402 item.classList.remove('expanded');
1403 }
1404 });
1405
1406 // Toggle this item
1407 parentItem.classList.toggle('expanded');
1408
1409 // If expanding, show the first sub-item's content and mark it active
1410 if (!wasExpanded) {
1411 const firstSubLink = parentItem.querySelector('.mxch-nav-sub-link');
1412 if (firstSubLink) {
1413 const firstTarget = firstSubLink.dataset.target;
1414 showSection(firstTarget);
1415 setActiveNav(firstSubLink);
1416 // Also mark parent as active
1417 this.classList.add('active');
1418 history.replaceState(null, null, '#' + firstTarget);
1419 }
1420 }
1421 }
1422 // If this is a nav link WITHOUT children (like API Keys, Tutorials)
1423 else if (this.classList.contains('mxch-nav-link') && !hasSubmenu && target) {
1424 // Collapse all expandable items
1425 navItems.forEach(item => {
1426 if (item.querySelector('.mxch-nav-sub')) {
1427 item.classList.remove('expanded');
1428 }
1429 });
1430
1431 // Show the section
1432 showSection(target);
1433 setActiveNav(this);
1434 history.replaceState(null, null, '#' + target);
1435 }
1436 // If this is a sub-link
1437 else if (this.classList.contains('mxch-nav-sub-link') && target) {
1438 // Ensure parent stays expanded
1439 if (parentItem) {
1440 parentItem.classList.add('expanded');
1441 }
1442 showSection(target);
1443 setActiveNav(this);
1444 history.replaceState(null, null, '#' + target);
1445 }
1446 });
1447 });
1448
1449 // Handle initial deep-link (?tab=<slug> takes precedence over #hash so the
1450 // Onboarding setup-step CTAs land on the right sub-tab). Whitelist of valid
1451 // section ids must stay in sync with the .mxch-section[id] values above.
1452 const validTabs = [
1453 'chatbot-ai-models', 'chatbot-behavior', 'chatbot-display',
1454 'chatbot-lead-capture', 'chatbot-quick-questions', 'chatbot-rate-limits',
1455 'api-keys', 'optimization', 'testing',
1456 'integrations-toolbar', 'integrations-loops', 'integrations-brave',
1457 'integrations-slack', 'integrations-telegram',
1458 'tutorials'
1459 ];
1460 function activateTab(target) {
1461 if (!target) return false;
1462 if (validTabs.indexOf(target) === -1) return false;
1463 const targetLink = document.querySelector('[data-target="' + target + '"]');
1464 if (!targetLink) return false;
1465 const parentItem = targetLink.closest('.mxch-nav-item');
1466 if (parentItem && targetLink.classList.contains('mxch-nav-sub-link')) {
1467 parentItem.classList.add('expanded');
1468 }
1469 showSection(target);
1470 setActiveNav(targetLink);
1471 return true;
1472 }
1473 const urlParams = new URLSearchParams(window.location.search);
1474 const tabParam = urlParams.get('tab');
1475 let tabActivated = false;
1476 if (tabParam) {
1477 tabActivated = activateTab(tabParam);
1478 // Invalid ?tab=… silently falls through to default (#hash or AI Models).
1479 }
1480 if (!tabActivated) {
1481 const hash = window.location.hash.slice(1);
1482 if (hash) {
1483 activateTab(hash);
1484 }
1485 }
1486
1487 // Rate limit accordion
1488 const rateLimitHeaders = document.querySelectorAll('.mxch-rate-limit-header');
1489 rateLimitHeaders.forEach(header => {
1490 header.addEventListener('click', function(e) {
1491 e.preventDefault();
1492 e.stopPropagation();
1493 const item = this.closest('.mxch-rate-limit-item');
1494 item.classList.toggle('expanded');
1495 });
1496 });
1497
1498 // Rate-limit "Custom…" toggle: show/hide the integer input below each
1499 // limit <select> based on whether __custom__ is the active option.
1500 document.querySelectorAll('.mxch-rate-limit-limit-select').forEach(sel => {
1501 const targetId = sel.getAttribute('data-mxch-custom-target');
1502 if (!targetId) return;
1503 const field = document.querySelector('[data-mxch-custom-for="' + targetId + '"]');
1504 if (!field) return;
1505 const sync = () => {
1506 if (sel.value === '__custom__') {
1507 field.hidden = false;
1508 } else {
1509 field.hidden = true;
1510 }
1511 };
1512 sel.addEventListener('change', sync);
1513 sync();
1514 });
1515
1516 // =====================================================
1517 // Mobile Menu Functionality
1518 // =====================================================
1519 const mobileMenuBtn = document.querySelector('.mxch-mobile-menu-btn');
1520 const mobileMenuClose = document.querySelector('.mxch-mobile-menu-close');
1521 const mobileMenu = document.querySelector('.mxch-mobile-menu');
1522 const mobileOverlay = document.querySelector('.mxch-mobile-overlay');
1523 const mobileNavLinks = document.querySelectorAll('.mxch-mobile-nav-link, .mxch-mobile-nav-sub-link');
1524
1525 function openMobileMenu() {
1526 mobileMenu.classList.add('open');
1527 mobileOverlay.classList.add('open');
1528 document.body.style.overflow = 'hidden';
1529 }
1530
1531 function closeMobileMenu() {
1532 mobileMenu.classList.remove('open');
1533 mobileOverlay.classList.remove('open');
1534 document.body.style.overflow = '';
1535 }
1536
1537 if (mobileMenuBtn) {
1538 mobileMenuBtn.addEventListener('click', openMobileMenu);
1539 }
1540
1541 if (mobileMenuClose) {
1542 mobileMenuClose.addEventListener('click', closeMobileMenu);
1543 }
1544
1545 if (mobileOverlay) {
1546 mobileOverlay.addEventListener('click', closeMobileMenu);
1547 }
1548
1549 // Mobile navigation links
1550 mobileNavLinks.forEach(link => {
1551 link.addEventListener('click', function(e) {
1552 e.preventDefault();
1553 e.stopPropagation();
1554
1555 const target = this.dataset.target;
1556 const parentId = this.dataset.parent;
1557
1558 // If this is an expandable parent link
1559 if (parentId && !target) {
1560 const subNav = document.querySelector('.mxch-mobile-nav-sub[data-parent="' + parentId + '"]');
1561 if (subNav) {
1562 const isExpanded = subNav.classList.contains('expanded');
1563 // Collapse all sub navs
1564 document.querySelectorAll('.mxch-mobile-nav-sub').forEach(nav => {
1565 nav.classList.remove('expanded');
1566 });
1567 document.querySelectorAll('.mxch-mobile-nav-link').forEach(l => {
1568 l.classList.remove('expanded');
1569 });
1570 // Toggle this one
1571 if (!isExpanded) {
1572 subNav.classList.add('expanded');
1573 this.classList.add('expanded');
1574 }
1575 }
1576 }
1577 // If this is a direct link or sub-link with a target
1578 else if (target) {
1579 // Update mobile nav active state
1580 document.querySelectorAll('.mxch-mobile-nav-link, .mxch-mobile-nav-sub-link').forEach(l => {
1581 l.classList.remove('active');
1582 });
1583 this.classList.add('active');
1584
1585 // Also update desktop sidebar nav
1586 const desktopLink = document.querySelector('.mxch-sidebar [data-target="' + target + '"]');
1587 if (desktopLink) {
1588 setActiveNav(desktopLink);
1589 const parentItem = desktopLink.closest('.mxch-nav-item');
1590 if (parentItem && desktopLink.classList.contains('mxch-nav-sub-link')) {
1591 parentItem.classList.add('expanded');
1592 }
1593 }
1594
1595 // Show section and close menu
1596 showSection(target);
1597 history.replaceState(null, null, '#' + target);
1598 closeMobileMenu();
1599 }
1600 });
1601 });
1602
1603 // Close mobile menu on escape key
1604 document.addEventListener('keydown', function(e) {
1605 if (e.key === 'Escape' && mobileMenu && mobileMenu.classList.contains('open')) {
1606 closeMobileMenu();
1607 }
1608 });
1609 });
1610 </script>
1611 <?php
1612 }
1613
1614 /**
1615 * Helper function to render a field with consistent wrapper
1616 */
1617 function mxchat_render_field_wrapper($id, $label, $callback, $description = '') {
1618 ?>
1619 <div class="mxch-field">
1620 <label class="mxch-field-label"><?php echo esc_html($label); ?></label>
1621 <div class="mxch-field-control">
1622 <?php $callback(); ?>
1623 </div>
1624 <?php if ($description): ?>
1625 <p class="mxch-field-description"><?php echo esc_html($description); ?></p>
1626 <?php endif; ?>
1627 </div>
1628 <?php
1629 }
1630
1631 /**
1632 * Render rate limits as collapsible accordion
1633 */
1634 function mxchat_render_rate_limits_accordion($admin_instance) {
1635 $all_options = get_option('mxchat_options', []);
1636 $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
1637 $timeframes = array(
1638 'hourly' => __('Per Hour', 'mxchat'),
1639 'daily' => __('Per Day', 'mxchat'),
1640 'weekly' => __('Per Week', 'mxchat'),
1641 'monthly' => __('Per Month', 'mxchat')
1642 );
1643
1644 $roles = wp_roles()->get_names();
1645 $roles['logged_out'] = __('Logged Out Users', 'mxchat');
1646
1647 // Whole-chatbot global cap (sits above per-role; defaults to unlimited so
1648 // existing installs are unchanged). Stored under mxchat_options['rate_limits_global'].
1649 $global_cfg = isset($all_options['rate_limits_global']) && is_array($all_options['rate_limits_global'])
1650 ? $all_options['rate_limits_global']
1651 : array();
1652 $global_limit_raw = isset($global_cfg['limit']) ? (string) $global_cfg['limit'] : 'unlimited';
1653 $global_timeframe = isset($global_cfg['timeframe']) ? (string) $global_cfg['timeframe'] : 'daily';
1654 $global_is_unlimited = ($global_limit_raw === '' || $global_limit_raw === 'unlimited');
1655 $global_is_preset = !$global_is_unlimited && in_array($global_limit_raw, $rate_limits, true);
1656 $global_select_value = $global_is_unlimited ? 'unlimited' : ($global_is_preset ? $global_limit_raw : '__custom__');
1657 $global_custom_value = (!$global_is_unlimited && !$global_is_preset && ctype_digit($global_limit_raw))
1658 ? $global_limit_raw
1659 : '';
1660
1661 echo '<div class="mxch-rate-limits">';
1662
1663 // --- Global cap card -------------------------------------------------
1664 ?>
1665 <div class="mxch-rate-limit-item mxch-rate-limit-global mxchat-autosave-section">
1666 <div class="mxch-rate-limit-header">
1667 <span class="mxch-rate-limit-role"><?php esc_html_e('Total chatbot message limit', 'mxchat'); ?></span>
1668 <div class="mxch-rate-limit-summary">
1669 <span class="mxch-rate-limit-badge mxch-rate-limit-global-badge">
1670 <?php
1671 if ($global_is_unlimited) {
1672 esc_html_e('Unlimited', 'mxchat');
1673 } else {
1674 $tf_label = isset($timeframes[$global_timeframe]) ? $timeframes[$global_timeframe] : $global_timeframe;
1675 echo esc_html(($global_custom_value !== '' ? $global_custom_value : $global_limit_raw) . ' / ' . $tf_label);
1676 }
1677 ?>
1678 </span>
1679 <svg class="mxch-rate-limit-arrow" 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"><polyline points="6 9 12 15 18 9"></polyline></svg>
1680 </div>
1681 </div>
1682 <div class="mxch-rate-limit-body">
1683 <p class="mxch-field-description" style="margin: 0 0 12px;">
1684 <?php esc_html_e('A single ceiling across all users and all roles, per timeframe. Independent of the per-role limits below. When both a per-role limit and this global cap are configured, whichever is hit first stops the conversation. Default is Unlimited — existing installs are unchanged.', 'mxchat'); ?>
1685 </p>
1686 <div class="mxch-rate-limit-controls">
1687 <div class="mxch-field">
1688 <label class="mxch-field-label" for="rate_limits_global_limit"><?php esc_html_e('Limit', 'mxchat'); ?></label>
1689 <select id="rate_limits_global_limit"
1690 name="mxchat_options[rate_limits_global][limit]"
1691 class="mxch-select mxchat-autosave-field mxch-rate-limit-limit-select"
1692 data-mxch-custom-target="rate_limits_global_limit_custom">
1693 <?php foreach ($rate_limits as $limit): ?>
1694 <option value="<?php echo esc_attr($limit); ?>" <?php selected($global_select_value, $limit); ?>>
1695 <?php echo esc_html($limit === 'unlimited' ? __('Unlimited', 'mxchat') : $limit); ?>
1696 </option>
1697 <?php endforeach; ?>
1698 <option value="__custom__" <?php selected($global_select_value, '__custom__'); ?>><?php esc_html_e('Custom…', 'mxchat'); ?></option>
1699 </select>
1700 </div>
1701 <div class="mxch-field mxch-rate-limit-custom-field" data-mxch-custom-for="rate_limits_global_limit_custom" <?php echo $global_select_value === '__custom__' ? '' : 'hidden'; ?>>
1702 <label class="mxch-field-label" for="rate_limits_global_limit_custom"><?php esc_html_e('Custom limit', 'mxchat'); ?></label>
1703 <input type="number"
1704 min="1"
1705 step="1"
1706 id="rate_limits_global_limit_custom"
1707 name="mxchat_options[rate_limits_global][limit_custom]"
1708 class="mxch-input mxchat-autosave-field mxch-rate-limit-custom-input"
1709 placeholder="e.g. 250"
1710 value="<?php echo esc_attr($global_custom_value); ?>" />
1711 </div>
1712 <div class="mxch-field">
1713 <label class="mxch-field-label" for="rate_limits_global_timeframe"><?php esc_html_e('Timeframe', 'mxchat'); ?></label>
1714 <select id="rate_limits_global_timeframe"
1715 name="mxchat_options[rate_limits_global][timeframe]"
1716 class="mxch-select mxchat-autosave-field">
1717 <?php foreach ($timeframes as $value => $label): ?>
1718 <option value="<?php echo esc_attr($value); ?>" <?php selected($global_timeframe, $value); ?>><?php echo esc_html($label); ?></option>
1719 <?php endforeach; ?>
1720 </select>
1721 </div>
1722 </div>
1723 <?php
1724 // --- Current usage readout (read-only; mirrors the integrator's
1725 // window-reset math at READ time — never writes the counter here).
1726 // v1 surfaces the DEFAULT bot's pool. TODO: per-bot selector if asked.
1727 $global_usage_limit_int = (!$global_is_unlimited && ctype_digit($global_limit_raw))
1728 ? (int) $global_limit_raw
1729 : 0;
1730 $usage_raw = get_option('mxchat_chat_limit_default_global', array('count' => 0, 'timestamp' => time()));
1731 $usage_count = isset($usage_raw['count']) ? (int) $usage_raw['count'] : 0;
1732 $usage_ts = isset($usage_raw['timestamp']) ? (int) $usage_raw['timestamp'] : time();
1733 $usage_windows = array('hourly' => 3600, 'daily' => 86400, 'weekly' => 604800, 'monthly' => 2592000);
1734 $usage_window = isset($usage_windows[$global_timeframe]) ? $usage_windows[$global_timeframe] : 86400;
1735 $usage_now = time();
1736 if (($usage_now - $usage_ts) >= $usage_window) {
1737 // Window elapsed since the stored timestamp → effective count is 0.
1738 $usage_count = 0;
1739 $usage_reset_at = $usage_now + $usage_window;
1740 } else {
1741 $usage_reset_at = $usage_ts + $usage_window;
1742 }
1743 $usage_left = max(0, $global_usage_limit_int - $usage_count);
1744 $usage_pct = ($global_usage_limit_int > 0)
1745 ? min(100, (int) round(($usage_count / $global_usage_limit_int) * 100))
1746 : 0;
1747 $usage_reset_nonce = wp_create_nonce('mxchat_reset_global_usage');
1748 ?>
1749 <div class="mxch-rate-limit-usage"
1750 id="mxch-global-usage"
1751 data-bot-id="default"
1752 data-reset-nonce="<?php echo esc_attr($usage_reset_nonce); ?>"
1753 <?php echo $global_is_unlimited ? 'hidden' : ''; ?>>
1754 <div class="mxch-rate-limit-usage-head">
1755 <span class="mxch-field-label" style="margin:0;"><?php esc_html_e('Current usage', 'mxchat'); ?></span>
1756 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" id="mxch-global-usage-reset"><?php esc_html_e('Reset counter', 'mxchat'); ?></button>
1757 </div>
1758 <div class="mxch-progress-bar">
1759 <div class="mxch-progress-bar-fill" id="mxch-global-usage-fill" style="width: <?php echo esc_attr($usage_pct); ?>%;"></div>
1760 </div>
1761 <p class="mxch-progress-label" id="mxch-global-usage-text" style="margin:0;">
1762 <?php
1763 printf(
1764 /* translators: 1: used count, 2: limit, 3: remaining, 4: human-readable time until reset */
1765 esc_html__('%1$s of %2$s used · %3$s left · resets in %4$s', 'mxchat'),
1766 esc_html(number_format_i18n($usage_count)),
1767 esc_html(number_format_i18n($global_usage_limit_int)),
1768 esc_html(number_format_i18n($usage_left)),
1769 esc_html(human_time_diff($usage_now, $usage_reset_at))
1770 );
1771 ?>
1772 </p>
1773 </div>
1774 <p class="mxch-field-description mxch-rate-limit-usage-unlimited"
1775 id="mxch-global-usage-unlimited"
1776 style="margin: 12px 0 0;"
1777 <?php echo $global_is_unlimited ? '' : 'hidden'; ?>>
1778 <?php esc_html_e('Unlimited — no cap. Usage tracking applies only when a numeric limit is set.', 'mxchat'); ?>
1779 </p>
1780 </div>
1781 </div>
1782 <?php
1783 // ---------------------------------------------------------------------
1784
1785 foreach ($roles as $role_id => $role_name) {
1786 $default_limit = ($role_id === 'logged_out') ? '10' : '100';
1787 $default_timeframe = 'daily';
1788 $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
1789
1790 $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
1791 ? (string) $all_options['rate_limits'][$role_id]['limit']
1792 : $default_limit;
1793
1794 $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
1795 ? $all_options['rate_limits'][$role_id]['timeframe']
1796 : $default_timeframe;
1797
1798 $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
1799 ? $all_options['rate_limits'][$role_id]['message']
1800 : $default_message;
1801
1802 $timeframe_label = isset($timeframes[$selected_timeframe]) ? $timeframes[$selected_timeframe] : $selected_timeframe;
1803
1804 // Custom-value handling: if the stored limit is not in the preset list
1805 // and not 'unlimited', it's a custom integer. The select shows __custom__
1806 // and the number input below carries the actual value.
1807 $is_preset_limit = in_array($selected_limit, $rate_limits, true);
1808 $role_select_value = $is_preset_limit ? $selected_limit : '__custom__';
1809 $role_custom_value = (!$is_preset_limit && ctype_digit($selected_limit)) ? $selected_limit : '';
1810 $custom_field_id = 'rate_limits_' . $role_id . '_limit_custom';
1811 ?>
1812 <div class="mxch-rate-limit-item">
1813 <div class="mxch-rate-limit-header">
1814 <span class="mxch-rate-limit-role"><?php echo esc_html($role_name); ?></span>
1815 <div class="mxch-rate-limit-summary">
1816 <span class="mxch-rate-limit-badge"><?php echo esc_html($selected_limit . ' / ' . $timeframe_label); ?></span>
1817 <svg class="mxch-rate-limit-arrow" 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"><polyline points="6 9 12 15 18 9"></polyline></svg>
1818 </div>
1819 </div>
1820 <div class="mxch-rate-limit-body mxchat-autosave-section">
1821 <div class="mxch-rate-limit-controls">
1822 <div class="mxch-field">
1823 <label class="mxch-field-label" for="rate_limits_<?php echo esc_attr($role_id); ?>_limit"><?php esc_html_e('Limit', 'mxchat'); ?></label>
1824 <select id="rate_limits_<?php echo esc_attr($role_id); ?>_limit"
1825 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][limit]"
1826 class="mxch-select mxchat-autosave-field mxch-rate-limit-limit-select"
1827 data-mxch-custom-target="<?php echo esc_attr($custom_field_id); ?>">
1828 <?php foreach ($rate_limits as $limit): ?>
1829 <option value="<?php echo esc_attr($limit); ?>" <?php selected($role_select_value, $limit); ?>>
1830 <?php echo esc_html($limit === 'unlimited' ? __('Unlimited', 'mxchat') : $limit); ?>
1831 </option>
1832 <?php endforeach; ?>
1833 <option value="__custom__" <?php selected($role_select_value, '__custom__'); ?>><?php esc_html_e('Custom…', 'mxchat'); ?></option>
1834 </select>
1835 </div>
1836 <div class="mxch-field mxch-rate-limit-custom-field" data-mxch-custom-for="<?php echo esc_attr($custom_field_id); ?>" <?php echo $role_select_value === '__custom__' ? '' : 'hidden'; ?>>
1837 <label class="mxch-field-label" for="<?php echo esc_attr($custom_field_id); ?>"><?php esc_html_e('Custom limit', 'mxchat'); ?></label>
1838 <input type="number"
1839 min="1"
1840 step="1"
1841 id="<?php echo esc_attr($custom_field_id); ?>"
1842 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][limit_custom]"
1843 class="mxch-input mxchat-autosave-field mxch-rate-limit-custom-input"
1844 placeholder="e.g. 250"
1845 value="<?php echo esc_attr($role_custom_value); ?>" />
1846 </div>
1847 <div class="mxch-field">
1848 <label class="mxch-field-label" for="rate_limits_<?php echo esc_attr($role_id); ?>_timeframe"><?php esc_html_e('Timeframe', 'mxchat'); ?></label>
1849 <select id="rate_limits_<?php echo esc_attr($role_id); ?>_timeframe"
1850 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][timeframe]"
1851 class="mxch-select mxchat-autosave-field">
1852 <?php foreach ($timeframes as $value => $label): ?>
1853 <option value="<?php echo esc_attr($value); ?>" <?php selected($selected_timeframe, $value); ?>><?php echo esc_html($label); ?></option>
1854 <?php endforeach; ?>
1855 </select>
1856 </div>
1857 </div>
1858 <div class="mxch-field">
1859 <label class="mxch-field-label" for="rate_limits_<?php echo esc_attr($role_id); ?>_message"><?php esc_html_e('Custom Message', 'mxchat'); ?></label>
1860 <textarea id="rate_limits_<?php echo esc_attr($role_id); ?>_message"
1861 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][message]"
1862 class="mxch-textarea mxchat-autosave-field"
1863 rows="2"
1864 placeholder="<?php esc_attr_e('Message shown when rate limit is reached', 'mxchat'); ?>"><?php echo esc_textarea($custom_message); ?></textarea>
1865 </div>
1866 </div>
1867 </div>
1868 <?php
1869 }
1870
1871 echo '</div>';
1872 }
1873