PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.13
MxChat – AI Chatbot & Content Generation for WordPress v3.2.13
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-onboarding-page.php

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

1,015 lines 66.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MxChat → Onboarding admin page (linear wizard).
4 *
5 * Plan: plan-mxchat-20260527-905439 — SUPERSEDES the 704e25 checklist UI.
6 *
7 * Five-step wizard:
8 * 1. Choose chat provider + model + API key
9 * 2. Choose embedding provider + model + API key (dedup if same as chat)
10 * 3. Seed the knowledge base (live-poll the KB row count)
11 * 4. Try Actions (optional)
12 * 5. Congrats + PRO add-ons (auto-graduates the menu item)
13 *
14 * Preserved from plan-f7c7d4: menu item, "Onboarding" naming, dismiss button,
15 * auto-graduate via mxchat_onboarding_dismissed, 301 from old slug, Stuck?
16 * Get help footer URLs, brand tokens, responsive 1280x800 / 390x844 quality bar.
17 *
18 * State storage:
19 * mxchat_onboarding_progress — assoc array { chat_model, embedding_model,
20 * knowledge_base, actions } booleans. Drives
21 * resume-where-you-left-off on reload.
22 * mxchat_onboarding_dismissed — reused from f7c7d4. Set when the user
23 * lands on Step 5 (auto-graduate) OR clicks
24 * Dismiss anywhere.
25 * mxchat_onboarding_auto_graduated — companion flag from f7c7d4; set when
26 * graduation was automatic rather than
27 * user-initiated.
28 * Provider/model/key options are the SAME ones Settings already uses
29 * (mxchat_options sub-keys: api_key for OpenAI, claude_api_key,
30 * xai_api_key, deepseek_api_key, gemini_api_key, openrouter_api_key,
31 * voyage_api_key, custom_provider_api_key + perplexity_api_key if/when
32 * that provider lands). No parallel key storage.
33 *
34 * Admin-chrome surgery (Onboarding page only): adds body class
35 * `mxchat-onboarding-focused` from class-mxchat-admin.php's admin_body_class
36 * hook so admin-onboarding-wizard.css can collapse the WP admin sidebar and
37 * suppress the bottom dashboard cards on THIS page only.
38 *
39 * @package MxChat
40 * @since 3.2.7
41 */
42
43 if (!defined('ABSPATH')) {
44 exit;
45 }
46
47 /**
48 * Read the persistent onboarding-dismissed flag.
49 */
50 function mxchat_onboarding_is_dismissed() {
51 return (bool) get_option('mxchat_onboarding_dismissed', 0);
52 }
53
54 /**
55 * Was the dismiss caused by auto-graduation rather than a user click?
56 */
57 function mxchat_onboarding_is_auto_graduated() {
58 return (bool) get_option('mxchat_onboarding_auto_graduated', 0);
59 }
60
61 /**
62 * Read the wizard progress state. Returns the four step booleans.
63 */
64 function mxchat_onboarding_get_progress() {
65 $defaults = array(
66 'chat_model' => false,
67 'behavior' => false,
68 'embedding_model' => false,
69 'knowledge_base' => false,
70 'actions' => false,
71 );
72 $stored = get_option('mxchat_onboarding_progress', array());
73 if (!is_array($stored)) {
74 $stored = array();
75 }
76 $out = array();
77 foreach ($defaults as $k => $v) {
78 $out[$k] = isset($stored[$k]) ? (bool) $stored[$k] : $v;
79 }
80 return $out;
81 }
82
83 /**
84 * Update one or more wizard progress flags. Merges into the stored array.
85 */
86 function mxchat_onboarding_set_progress($changes) {
87 $current = mxchat_onboarding_get_progress();
88 foreach ((array) $changes as $k => $v) {
89 if (array_key_exists($k, $current)) {
90 $current[$k] = (bool) $v;
91 }
92 }
93 update_option('mxchat_onboarding_progress', $current);
94 return $current;
95 }
96
97 /**
98 * Existing AJAX: user-initiated dismiss. Preserved verbatim from f7c7d4.
99 */
100 add_action('wp_ajax_mxchat_dismiss_onboarding', 'mxchat_handle_dismiss_onboarding');
101 function mxchat_handle_dismiss_onboarding() {
102 if (!current_user_can('manage_options')) {
103 wp_send_json_error(array('message' => __('You do not have permission to do this.', 'mxchat')), 403);
104 }
105 check_ajax_referer('mxchat_dismiss_onboarding', 'nonce');
106
107 update_option('mxchat_onboarding_dismissed', 1);
108 update_option('mxchat_onboarding_auto_graduated', 0);
109
110 wp_send_json_success(array('dismissed' => true));
111 }
112
113 /**
114 * admin_post: "Show MxChat Onboarding again" — clears both dismissed flag
115 * AND wizard progress so the user gets a fresh wizard. Preserved from f7c7d4
116 * with the added progress-reset.
117 */
118 add_action('admin_post_mxchat_unhide_onboarding', 'mxchat_handle_unhide_onboarding');
119 function mxchat_handle_unhide_onboarding() {
120 if (!current_user_can('manage_options')) {
121 wp_die(esc_html__('You do not have permission to do this.', 'mxchat'));
122 }
123 check_admin_referer('mxchat_unhide_onboarding');
124
125 delete_option('mxchat_onboarding_dismissed');
126 delete_option('mxchat_onboarding_auto_graduated');
127
128 wp_safe_redirect(admin_url('admin.php?page=mxchat-onboarding'));
129 exit;
130 }
131
132 /**
133 * admin_init: legacy slug 301 redirect (preserved from f7c7d4).
134 */
135 add_action('admin_init', 'mxchat_onboarding_legacy_slug_redirect');
136 function mxchat_onboarding_legacy_slug_redirect() {
137 if (!isset($_GET['page'])) {
138 return;
139 }
140 if ($_GET['page'] !== 'mxchat-dashboard') {
141 return;
142 }
143 wp_safe_redirect(admin_url('admin.php?page=mxchat-onboarding'), 301);
144 exit;
145 }
146
147 /**
148 * Provider catalog used by Steps 1 and 2. Wraps the canonical catalog in
149 * includes/class-mxchat-model-catalog.php (plan-d14e89) so a single edit
150 * there flows through to the Onboarding wizard's dropdowns. Shape preserved
151 * for back-compat:
152 * [ slug => [ label, key_option, chat_models{}, embedding_models{} ] ]
153 * Plus an extra `requires_key_to_load_models` flag per provider (true for
154 * OpenRouter) so the wizard can disable that provider in its picker.
155 */
156 function mxchat_onboarding_provider_catalog() {
157 if (!class_exists('MxChat_Model_Catalog')) {
158 require_once plugin_dir_path(__FILE__) . 'class-mxchat-model-catalog.php';
159 }
160 $chat = MxChat_Model_Catalog::chat_models();
161 $emb = MxChat_Model_Catalog::embedding_models();
162
163 $out = array();
164 $all_slugs = array_unique(array_merge(array_keys($chat), array_keys($emb)));
165 foreach ($all_slugs as $slug) {
166 $chat_models = array();
167 $requires_key = false;
168 $disable_onb = false;
169 if (isset($chat[$slug])) {
170 foreach ($chat[$slug]['models'] as $id => $entry) {
171 $chat_models[$id] = $entry['label'];
172 }
173 $requires_key = !empty($chat[$slug]['requires_key_to_load_models']);
174 $disable_onb = !empty($chat[$slug]['disable_in_onboarding']);
175 }
176 $embed_models = array();
177 if (isset($emb[$slug])) {
178 foreach ($emb[$slug]['models'] as $id => $entry) {
179 $embed_models[$id] = $entry['label'];
180 }
181 }
182 $out[$slug] = array(
183 'label' => MxChat_Model_Catalog::provider_label($slug),
184 'key_option' => MxChat_Model_Catalog::key_option_for_provider($slug),
185 'chat_models' => $chat_models,
186 'embedding_models' => $embed_models,
187 'requires_key_to_load_models' => $requires_key,
188 'disable_in_onboarding' => ($disable_onb || $requires_key),
189 );
190 }
191 return $out;
192 }
193
194 /**
195 * Infer the provider slug from a chat model id, mirroring the rule of thumb
196 * the model picker uses. Returns 'openai' / 'claude' / 'gemini' / 'xai' /
197 * 'deepseek' / 'openrouter' / 'custom' or '' if unknown.
198 */
199 function mxchat_onboarding_provider_from_model($model_id) {
200 $m = strtolower((string) $model_id);
201 if ($m === '') return '';
202 if (strpos($m, 'gpt-') === 0 || strpos($m, 'o1-') === 0 || strpos($m, 'o3-') === 0) return 'openai';
203 if (strpos($m, 'claude-') === 0) return 'claude';
204 if (strpos($m, 'gemini-') === 0) return 'gemini';
205 if (strpos($m, 'grok') === 0) return 'xai';
206 if (strpos($m, 'deepseek') === 0) return 'deepseek';
207 if (strpos($m, 'sonar') === 0 || strpos($m, 'pplx-') === 0) return 'perplexity';
208 if ($m === 'openrouter' || strpos($m, '/') !== false) return 'openrouter';
209 if (strpos($m, 'custom') === 0) return 'custom';
210 return '';
211 }
212
213 /**
214 * Mirror for embedding models.
215 */
216 function mxchat_onboarding_provider_from_embedding_model($model_id) {
217 $m = strtolower((string) $model_id);
218 if ($m === '') return '';
219 if (strpos($m, 'text-embedding-') === 0) return 'openai';
220 if (strpos($m, 'voyage') === 0) return 'voyage';
221 if (strpos($m, 'gemini-embedding') === 0) return 'gemini';
222 return '';
223 }
224
225 /**
226 * Whether the given provider has a non-empty API key saved.
227 */
228 function mxchat_onboarding_provider_has_key($provider_slug) {
229 $catalog = mxchat_onboarding_provider_catalog();
230 if (!isset($catalog[$provider_slug])) return false;
231 $opts = get_option('mxchat_options', array());
232 if (!is_array($opts)) $opts = array();
233 $field = $catalog[$provider_slug]['key_option'];
234 return !empty($opts[$field]) && trim((string) $opts[$field]) !== '';
235 }
236
237 /**
238 * KB row count. The canonical KB content table is
239 * {$wpdb->prefix}mxchat_system_prompt_content — see mxchat-basic.php:716
240 * where the table is created. An earlier draft of this function queried
241 * mxchat_embeddings (which doesn't exist on most installs), causing Step 3
242 * to always report "No knowledge base items yet" even after items had been
243 * added. Plan-d14e89 Issue 6.
244 */
245 function mxchat_onboarding_kb_count() {
246 global $wpdb;
247 $table = $wpdb->prefix . 'mxchat_system_prompt_content';
248 $exists = (bool) $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
249 if (!$exists) return 0;
250 return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
251 }
252
253 /**
254 * AJAX: live KB status poll for Step 3. Returns { count: N }.
255 * Nonce-protected per plan spec (use per-request nonce path from 6a68c9 —
256 * here the nonce is issued fresh in the page renderer, NOT cached, since
257 * admin pages aren't page-cached).
258 */
259 add_action('wp_ajax_mxchat_onboarding_kb_status', 'mxchat_onboarding_ajax_kb_status');
260 function mxchat_onboarding_ajax_kb_status() {
261 if (!current_user_can('manage_options')) {
262 wp_send_json_error(array('message' => __('You do not have permission.', 'mxchat')), 403);
263 }
264 check_ajax_referer('mxchat_onboarding_nonce', 'nonce');
265 wp_send_json_success(array('count' => mxchat_onboarding_kb_count()));
266 }
267
268 /**
269 * AJAX: save Step 1 (chat) or Step 2 (embedding) selection. Persists
270 * provider/model/key into the SAME mxchat_options sub-keys Settings uses
271 * (no parallel storage), then flips the corresponding progress flag.
272 *
273 * Expected POST: which=chat|embedding, provider=<slug>, model=<id>,
274 * api_key=<string optional — only set if a fresh key was entered>.
275 */
276 add_action('wp_ajax_mxchat_onboarding_save_step', 'mxchat_onboarding_ajax_save_step');
277 function mxchat_onboarding_ajax_save_step() {
278 if (!current_user_can('manage_options')) {
279 wp_send_json_error(array('message' => __('You do not have permission.', 'mxchat')), 403);
280 }
281 check_ajax_referer('mxchat_onboarding_nonce', 'nonce');
282
283 $which = isset($_POST['which']) ? sanitize_key($_POST['which']) : '';
284
285 // Behavior step branch (plan-a2e4d6) — saves system_prompt_instructions
286 // into the SAME mxchat_options sub-key Settings uses, no parallel storage.
287 if ($which === 'behavior') {
288 $instructions = isset($_POST['system_prompt_instructions'])
289 ? sanitize_textarea_field(wp_unslash($_POST['system_prompt_instructions']))
290 : '';
291 $opts = get_option('mxchat_options', array());
292 if (!is_array($opts)) $opts = array();
293 $opts['system_prompt_instructions'] = $instructions;
294 update_option('mxchat_options', $opts);
295 mxchat_onboarding_set_progress(array('behavior' => true));
296 wp_send_json_success(array(
297 'progress' => mxchat_onboarding_get_progress(),
298 ));
299 }
300
301 $provider = isset($_POST['provider']) ? sanitize_key($_POST['provider']) : '';
302 $model = isset($_POST['model']) ? sanitize_text_field(wp_unslash($_POST['model'])) : '';
303 $api_key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : '';
304
305 $catalog = mxchat_onboarding_provider_catalog();
306 if (!in_array($which, array('chat', 'embedding'), true)) {
307 wp_send_json_error(array('message' => __('Invalid step.', 'mxchat')), 400);
308 }
309 if (!isset($catalog[$provider])) {
310 wp_send_json_error(array('message' => __('Unknown provider.', 'mxchat')), 400);
311 }
312 $model_list = $which === 'chat' ? $catalog[$provider]['chat_models'] : $catalog[$provider]['embedding_models'];
313 if (!isset($model_list[$model])) {
314 wp_send_json_error(array('message' => __('Model not valid for this provider.', 'mxchat')), 400);
315 }
316
317 $opts = get_option('mxchat_options', array());
318 if (!is_array($opts)) $opts = array();
319
320 // Persist the key if a fresh one was sent. Empty string means "no change"
321 // (the UI sends '' when the user is reusing an already-saved key).
322 if ($api_key !== '') {
323 $opts[$catalog[$provider]['key_option']] = $api_key;
324 }
325
326 if ($which === 'chat') {
327 $opts['model'] = $model;
328 } else {
329 $opts['embedding_model'] = $model;
330 }
331 update_option('mxchat_options', $opts);
332
333 $progress_key = $which === 'chat' ? 'chat_model' : 'embedding_model';
334 mxchat_onboarding_set_progress(array($progress_key => true));
335
336 wp_send_json_success(array(
337 'progress' => mxchat_onboarding_get_progress(),
338 ));
339 }
340
341 /**
342 * AJAX: mark a step done (used by Step 4 actions Skip/Continue, and Step 3
343 * Continue once KB is populated). No-op if the step name is unknown.
344 */
345 add_action('wp_ajax_mxchat_onboarding_mark_step', 'mxchat_onboarding_ajax_mark_step');
346 function mxchat_onboarding_ajax_mark_step() {
347 if (!current_user_can('manage_options')) {
348 wp_send_json_error(array('message' => __('You do not have permission.', 'mxchat')), 403);
349 }
350 check_ajax_referer('mxchat_onboarding_nonce', 'nonce');
351
352 $step = isset($_POST['step']) ? sanitize_key($_POST['step']) : '';
353 $valid = array('chat_model', 'behavior', 'embedding_model', 'knowledge_base', 'actions');
354 if (!in_array($step, $valid, true)) {
355 wp_send_json_error(array('message' => __('Unknown step.', 'mxchat')), 400);
356 }
357 mxchat_onboarding_set_progress(array($step => true));
358 wp_send_json_success(array('progress' => mxchat_onboarding_get_progress()));
359 }
360
361 /**
362 * AJAX: auto-graduate on Step 5 land. Sets dismissed + auto_graduated flags
363 * (same flags f7c7d4 used) so the menu item disappears on next admin nav.
364 */
365 add_action('wp_ajax_mxchat_onboarding_auto_graduate', 'mxchat_onboarding_ajax_auto_graduate');
366 function mxchat_onboarding_ajax_auto_graduate() {
367 if (!current_user_can('manage_options')) {
368 wp_send_json_error(array('message' => __('You do not have permission.', 'mxchat')), 403);
369 }
370 check_ajax_referer('mxchat_onboarding_nonce', 'nonce');
371
372 update_option('mxchat_onboarding_dismissed', 1);
373 update_option('mxchat_onboarding_auto_graduated', 1);
374 wp_send_json_success(array('graduated' => true));
375 }
376
377 /**
378 * Render the Onboarding page — wizard layout. Entry point wired from
379 * class-mxchat-admin.php.
380 */
381 function mxchat_render_onboarding_page() {
382 if (!current_user_can('manage_options')) {
383 wp_die(esc_html__('You do not have permission to access this page.', 'mxchat'));
384 }
385
386 $opts = get_option('mxchat_options', array());
387 if (!is_array($opts)) $opts = array();
388
389 $progress = mxchat_onboarding_get_progress();
390 $catalog = mxchat_onboarding_provider_catalog();
391
392 // Compute saved-key state per provider so the JS can short-circuit the
393 // API-key input to the "already saved" checkmark on initial render.
394 $key_state = array();
395 foreach ($catalog as $slug => $entry) {
396 $key_state[$slug] = mxchat_onboarding_provider_has_key($slug);
397 }
398
399 // Current selections (pre-populate the wizard if user already had values
400 // in Settings before opening Onboarding).
401 $current_chat_model = isset($opts['model']) ? (string) $opts['model'] : '';
402 $current_chat_provider = mxchat_onboarding_provider_from_model($current_chat_model);
403 $current_embed_model = isset($opts['embedding_model']) ? (string) $opts['embedding_model'] : '';
404 $current_embed_provider = mxchat_onboarding_provider_from_embedding_model($current_embed_model);
405
406 // Pre-credit logic — run BEFORE deciding the initial step so the landing
407 // step reflects pre-existing Settings values. The wizard is six steps now
408 // (plan-a2e4d6 inserted "AI Behavior" at position 2):
409 // 1. Chat model → progress.chat_model
410 // 2. AI Behavior → progress.behavior (NEW)
411 // 3. Embedding → progress.embedding_model
412 // 4. Knowledge base→ progress.knowledge_base
413 // 5. Actions → progress.actions
414 // 6. Congrats → no progress flag (all-five-complete view)
415
416 // Pre-credit Step 1 (chat) if a chat model + key are already saved.
417 if (!$progress['chat_model'] && $current_chat_model !== '' && $current_chat_provider !== '' && !empty($key_state[$current_chat_provider])) {
418 mxchat_onboarding_set_progress(array('chat_model' => true));
419 $progress['chat_model'] = true;
420 }
421 // Pre-credit Step 2 (behavior) if system_prompt_instructions is non-empty
422 // — the user already set this in Settings before opening Onboarding.
423 $current_instructions = isset($opts['system_prompt_instructions']) ? (string) $opts['system_prompt_instructions'] : '';
424 if (!$progress['behavior'] && trim($current_instructions) !== '') {
425 mxchat_onboarding_set_progress(array('behavior' => true));
426 $progress['behavior'] = true;
427 }
428 // Pre-credit Step 3 (embedding) if model + key already saved.
429 if (!$progress['embedding_model'] && $current_embed_model !== '' && $current_embed_provider !== '' && !empty($key_state[$current_embed_provider])) {
430 mxchat_onboarding_set_progress(array('embedding_model' => true));
431 $progress['embedding_model'] = true;
432 }
433 // Pre-credit Step 4 (KB) if rows already exist.
434 if (!$progress['knowledge_base'] && mxchat_onboarding_kb_count() > 0) {
435 mxchat_onboarding_set_progress(array('knowledge_base' => true));
436 $progress['knowledge_base'] = true;
437 }
438
439 // First-not-done step is where we land on load. With all five flags true,
440 // we land on Step 6 (Congrats).
441 if (!$progress['chat_model']) $initial_step = 1;
442 elseif (!$progress['behavior']) $initial_step = 2;
443 elseif (!$progress['embedding_model']) $initial_step = 3;
444 elseif (!$progress['knowledge_base']) $initial_step = 4;
445 elseif (!$progress['actions']) $initial_step = 5;
446 else $initial_step = 6;
447
448 $plugin_url = plugin_dir_url(dirname(__FILE__));
449 $dismiss_nonce = wp_create_nonce('mxchat_dismiss_onboarding');
450 $wizard_nonce = wp_create_nonce('mxchat_onboarding_nonce');
451
452 $current_slug = isset($_GET['page']) ? sanitize_key($_GET['page']) : 'mxchat-onboarding';
453 $nav_items = array(
454 array('slug' => 'mxchat-onboarding', 'label' => __('Onboarding', 'mxchat'), 'svg' => '<polyline points="3 9 12 2 21 9 21 21 14 21 14 14 10 14 10 21 3 21 3 9"/>'),
455 array('slug' => 'mxchat-settings', 'label' => __('Settings', 'mxchat'), 'svg' => '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>'),
456 array('slug' => 'mxchat-prompts', 'label' => __('Knowledge', 'mxchat'), 'svg' => '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>'),
457 array('slug' => 'mxchat-transcripts', 'label' => __('Transcripts', 'mxchat'), 'svg' => '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>'),
458 array('slug' => 'mxchat-actions', 'label' => __('Actions', 'mxchat'), 'svg' => '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>'),
459 array('slug' => 'mxchat-content', 'label' => __('Content', 'mxchat'), 'svg' => '<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"/>'),
460 array('slug' => 'mxchat-api-access', 'label' => __('API Access', 'mxchat'), 'svg' => '<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"/>'),
461 array('slug' => 'mxchat-activation', 'label' => __('Pro & Extensions','mxchat'), 'svg' => '<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"/>'),
462 );
463
464 // Build a JS-shaped catalog for the wizard's client-side step machine.
465 $js_catalog = array();
466 foreach ($catalog as $slug => $entry) {
467 $js_catalog[$slug] = array(
468 'label' => $entry['label'],
469 'chatModels' => $entry['chat_models'],
470 'embeddingModels' => $entry['embedding_models'],
471 'hasKey' => $key_state[$slug],
472 'requiresKeyToLoadModels' => !empty($entry['requires_key_to_load_models']),
473 );
474 }
475
476 // Step metadata for the clickable pill indicator (plan-a2e4d6). Each entry:
477 // - id: progress flag this step gates (null for Congrats)
478 // - label: short pill label
479 // - title: full step heading (also exposed for the legacy "Step N of M"
480 // text-fallback)
481 $steps_meta = array(
482 array('num' => 1, 'flag' => 'chat_model', 'label' => __('Chat model', 'mxchat'), 'title' => __('Choose your chat model', 'mxchat')),
483 array('num' => 2, 'flag' => 'behavior', 'label' => __('Behavior', 'mxchat'), 'title' => __('Tell your chatbot how to behave', 'mxchat')),
484 array('num' => 3, 'flag' => 'embedding_model', 'label' => __('Embedding', 'mxchat'), 'title' => __('Choose your embedding model', 'mxchat')),
485 array('num' => 4, 'flag' => 'knowledge_base', 'label' => __('Knowledge', 'mxchat'), 'title' => __('Add to your knowledge base', 'mxchat')),
486 array('num' => 5, 'flag' => 'actions', 'label' => __('Actions', 'mxchat'), 'title' => __('Try MxChat Actions', 'mxchat')),
487 array('num' => 6, 'flag' => null, 'label' => __('Done', 'mxchat'), 'title' => __('You\'re set up', 'mxchat')),
488 );
489
490 $wizard_config = array(
491 'ajaxUrl' => admin_url('admin-ajax.php'),
492 'nonce' => $wizard_nonce,
493 'catalog' => $js_catalog,
494 'initialStep' => (int) $initial_step,
495 'progress' => $progress,
496 'currentChatProvider' => $current_chat_provider,
497 'currentChatModel' => $current_chat_model,
498 'currentEmbedProvider'=> $current_embed_provider,
499 'currentEmbedModel' => $current_embed_model,
500 'currentInstructions' => $current_instructions,
501 'kbCount' => mxchat_onboarding_kb_count(),
502 'stepsMeta' => $steps_meta,
503 'urls' => array(
504 'kb' => admin_url('admin.php?page=mxchat-prompts'),
505 'actions' => admin_url('admin.php?page=mxchat-actions'),
506 'apiKeys' => admin_url('admin.php?page=mxchat-settings&tab=api-keys'),
507 'homepage' => home_url('/'),
508 'proAddons' => 'https://mxchat.ai/add-ons/',
509 'settingsTesting' => admin_url('admin.php?page=mxchat-settings#testing'),
510 'settingsDisplay' => admin_url('admin.php?page=mxchat-settings#chatbot-display'),
511 ),
512 'strings' => array(
513 'keyAlreadySaved' => __('%s API key already saved.', 'mxchat'),
514 'embedKeyReused' => __('You\'ve already added your %s API key — using it for embeddings too.', 'mxchat'),
515 'replaceKey' => __('Replace key', 'mxchat'),
516 'saveKey' => __('Save key', 'mxchat'),
517 'saving' => __('Saving…', 'mxchat'),
518 'saved' => __('Saved', 'mxchat'),
519 'saveError' => __('Save failed. Try again or set the key in Settings → API Keys.', 'mxchat'),
520 'kbNone' => __('No knowledge base items yet.', 'mxchat'),
521 /* translators: %d = number of KB items */
522 'kbFoundOne' => __('Found 1 knowledge base item.', 'mxchat'),
523 /* translators: %d = number of KB items */
524 'kbFoundMany' => __('Found %d knowledge base items.', 'mxchat'),
525 'manageAllKeys' => __('You can also manage all your keys later in API Keys settings.', 'mxchat'),
526 'selectProvider' => __('— Select a provider —', 'mxchat'),
527 'selectModel' => __('— Select a model —', 'mxchat'),
528 'disabledHintOpenrouter' => __('OpenRouter requires setup in the main Settings page — finish onboarding, then configure it there.', 'mxchat'),
529 'disabledHintCustom' => __('Custom (OpenAI-compatible) providers need a Base URL and model configured in Settings → API Keys — finish onboarding, then set it up there.', 'mxchat'),
530 'shortcodeCopied' => __('Copied!', 'mxchat'),
531 'shortcodeCopy' => __('Copy', 'mxchat'),
532 ),
533 );
534 ?>
535 <div class="mxch-onboarding-wrapper mxch-onboarding-wizard-wrapper">
536
537 <main class="mxch-content">
538
539 <div class="mxch-section active">
540 <div class="mxch-content-header">
541 <h1 class="mxch-content-title"><?php esc_html_e('Onboarding', 'mxchat'); ?></h1>
542 <p class="mxch-content-subtitle">
543 <?php esc_html_e('Six quick steps to get your MxChat chatbot live.', 'mxchat'); ?>
544 </p>
545 </div>
546
547 <div class="mxch-card mxch-wizard-card" id="mxch-onboarding-wizard">
548 <div class="mxch-wizard-progress" aria-hidden="false">
549 <!-- Clickable pill step indicator (plan-a2e4d6). The JS attaches
550 click handlers + sets aria-current="step" on the active pill +
551 toggles .is-complete / .is-current / .is-future as the user
552 advances. Future steps are non-clickable. -->
553 <nav class="mxch-wizard-pillnav" role="tablist" aria-label="<?php esc_attr_e('Onboarding steps', 'mxchat'); ?>">
554 <?php foreach ($steps_meta as $sm):
555 $is_complete = ($sm['flag'] !== null && !empty($progress[$sm['flag']]))
556 || ($sm['flag'] === null && (int) $initial_step >= 6);
557 $is_current = ((int) $initial_step === (int) $sm['num']);
558 $cls = 'mxch-wizard-pill';
559 if ($is_complete && !$is_current) $cls .= ' is-complete';
560 if ($is_current) $cls .= ' is-current';
561 if (!$is_complete && !$is_current) $cls .= ' is-future';
562 ?>
563 <button type="button"
564 class="<?php echo esc_attr($cls); ?>"
565 data-mxch-pill-step="<?php echo (int) $sm['num']; ?>"
566 <?php if ($is_current) echo 'aria-current="step"'; ?>
567 <?php if (!$is_complete && !$is_current) echo 'disabled aria-disabled="true"'; ?>>
568 <span class="mxch-wizard-pill-num" aria-hidden="true">
569 <svg class="mxch-wizard-pill-check" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
570 <span class="mxch-wizard-pill-num-text"><?php echo (int) $sm['num']; ?></span>
571 </span>
572 <span class="mxch-wizard-pill-label"><?php echo esc_html($sm['label']); ?></span>
573 </button>
574 <?php endforeach; ?>
575 </nav>
576 <div class="mxch-wizard-progress-label">
577 <span class="mxch-wizard-step-label"><?php esc_html_e('Step', 'mxchat'); ?> <span data-mxch-current-step>1</span> <?php esc_html_e('of', 'mxchat'); ?> 6</span>
578 <span class="mxch-wizard-step-name" data-mxch-step-name></span>
579 </div>
580 <div class="mxch-wizard-progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100">
581 <div class="mxch-wizard-progress-fill" data-mxch-progress-fill></div>
582 </div>
583 </div>
584
585 <!-- STEP 1 chat provider + model + key -->
586 <section class="mxch-wizard-step" data-step="1" hidden>
587 <h2 class="mxch-wizard-step-title"><?php esc_html_e('Choose your chat model', 'mxchat'); ?></h2>
588 <p class="mxch-wizard-step-explainer">
589 <?php esc_html_e('This is the AI that will write replies in your chatbot. Different providers have different strengths and pricing. You can change this any time.', 'mxchat'); ?>
590 </p>
591
592 <div class="mxch-field">
593 <label class="mxch-field-label" for="mxch-wiz-chat-provider"><?php esc_html_e('Provider', 'mxchat'); ?></label>
594 <select class="mxch-wiz-select" id="mxch-wiz-chat-provider" data-mxch-which="chat" data-mxch-role="provider">
595 <option value=""><?php echo esc_html($wizard_config['strings']['selectProvider']); ?></option>
596 <?php foreach ($catalog as $slug => $entry):
597 if (empty($entry['chat_models'])) continue;
598 // Plan-23987f — OpenRouter (requires-key flow) AND
599 // Custom (needs Base URL + model id in API Keys tab)
600 // are both disabled in the wizard's static dropdown
601 // via the unified `disable_in_onboarding` flag.
602 $opt_disabled = !empty($entry['disable_in_onboarding']);
603 $opt_label = $entry['label'];
604 if ($opt_disabled) {
605 /* translators: %s = provider label, e.g. OpenRouter */
606 $opt_label = sprintf(__('%s — configure in Settings', 'mxchat'), $entry['label']);
607 }
608 ?>
609 <option value="<?php echo esc_attr($slug); ?>" data-mxch-disabled-onboarding="<?php echo $opt_disabled ? '1' : '0'; ?>" <?php selected($current_chat_provider, $slug); ?><?php echo $opt_disabled ? ' disabled' : ''; ?>><?php echo esc_html($opt_label); ?></option>
610 <?php endforeach; ?>
611 </select>
612 <p class="mxch-field-hint mxch-wiz-disabled-provider-hint">
613 <?php esc_html_e('OpenRouter and Custom (OpenAI-compatible) providers need extra configuration — finish onboarding, then set them up in Settings → API Keys.', 'mxchat'); ?>
614 </p>
615 </div>
616
617 <div class="mxch-field mxch-wiz-model-field" data-mxch-which="chat" hidden>
618 <label class="mxch-field-label" for="mxch-wiz-chat-model"><?php esc_html_e('Model', 'mxchat'); ?></label>
619 <select class="mxch-wiz-select" id="mxch-wiz-chat-model" data-mxch-which="chat" data-mxch-role="model"></select>
620 </div>
621
622 <div class="mxch-field mxch-wiz-key-field" data-mxch-which="chat" hidden>
623 <label class="mxch-field-label" data-mxch-key-label></label>
624 <p class="mxch-field-description"><?php esc_html_e('An API key is a password from your AI provider that lets MxChat talk to them.', 'mxchat'); ?></p>
625 <div class="mxch-wiz-key-row">
626 <input type="password" class="mxch-wiz-key-input" data-mxch-which="chat" autocomplete="new-password" data-lpignore="true" />
627 <button type="button" class="mxch-btn mxch-btn-primary mxch-btn-sm mxch-wiz-key-save" data-mxch-which="chat"><?php echo esc_html($wizard_config['strings']['saveKey']); ?></button>
628 </div>
629 <p class="mxch-field-hint"><?php echo wp_kses_post(sprintf(
630 /* translators: %s = link to API Keys settings */
631 __('You can also manage all your keys later in %s.', 'mxchat'),
632 '<a href="' . esc_url($wizard_config['urls']['apiKeys']) . '">' . esc_html__('API Keys settings', 'mxchat') . '</a>'
633 )); ?></p>
634 </div>
635
636 <div class="mxch-wiz-key-saved" data-mxch-which="chat" hidden>
637 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
638 <span data-mxch-key-saved-text></span>
639 <button type="button" class="mxch-wiz-replace-key-link" data-mxch-which="chat"><?php echo esc_html($wizard_config['strings']['replaceKey']); ?></button>
640 </div>
641
642 <div class="mxch-wiz-error" data-mxch-which="chat" hidden></div>
643
644 <div class="mxch-wizard-actions">
645 <button type="button" class="mxch-btn mxch-btn-primary mxch-wiz-continue" data-mxch-from-step="1" disabled><?php esc_html_e('Continue', 'mxchat'); ?></button>
646 </div>
647 </section>
648
649 <!-- STEP 2 AI Behavior (system_prompt_instructions) plan-a2e4d6.
650 Re-uses the SAME mxchat_options['system_prompt_instructions']
651 binding Settings Behavior uses. No parallel storage. The
652 Sample Instructions modal is rendered once at the bottom of
653 this page (after the wizard card) and shares the same DOM
654 ids (mxchatViewSampleBtn / mxchatSampleModal) so the existing
655 modal CSS in admin-style.css just works. -->
656 <section class="mxch-wizard-step" data-step="2" hidden>
657 <h2 class="mxch-wizard-step-title"><?php esc_html_e('Tell your chatbot how to behave', 'mxchat'); ?></h2>
658 <p class="mxch-wizard-step-explainer">
659 <?php esc_html_e('These are the instructions your AI follows for every reply. Think of it as the chatbot\'s job description — what role it plays, what tone it uses, what topics it stays on or avoids. You can edit this any time from Settings → Behavior.', 'mxchat'); ?>
660 </p>
661
662 <div class="mxch-field">
663 <label class="mxch-field-label" for="mxch-wiz-behavior-textarea"><?php esc_html_e('AI Instructions', 'mxchat'); ?></label>
664 <textarea id="mxch-wiz-behavior-textarea" class="mxch-wiz-behavior-textarea" rows="6" placeholder="<?php esc_attr_e('Describe how your chatbot should respond — its role, tone, and topics to focus on or avoid.', 'mxchat'); ?>"><?php echo esc_textarea($current_instructions); ?></textarea>
665 <p class="mxch-field-hint">
666 <?php esc_html_e('Optional — leave blank to use sensible defaults. You can refine this later.', 'mxchat'); ?>
667 </p>
668 </div>
669
670 <div class="mxch-wiz-behavior-actions-row">
671 <button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">
672 <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">
673 <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
674 <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
675 </svg>
676 <?php esc_html_e('View Sample Instructions', 'mxchat'); ?>
677 </button>
678 </div>
679
680 <p class="mxch-wiz-behavior-examples-label">
681 <?php esc_html_e('Or start from one of these:', 'mxchat'); ?>
682 </p>
683 <div class="mxch-wiz-behavior-examples">
684 <button type="button" class="mxch-wiz-behavior-example" data-mxch-example="helpful">
685 <span class="mxch-wiz-behavior-example-title"><?php esc_html_e('Helpful website assistant', 'mxchat'); ?></span>
686 <span class="mxch-wiz-behavior-example-desc"><?php esc_html_e('General-purpose, concise, sticks to your site\'s content.', 'mxchat'); ?></span>
687 </button>
688 <button type="button" class="mxch-wiz-behavior-example" data-mxch-example="sales">
689 <span class="mxch-wiz-behavior-example-title"><?php esc_html_e('Sales-focused product expert', 'mxchat'); ?></span>
690 <span class="mxch-wiz-behavior-example-desc"><?php esc_html_e('Highlights features, suggests products, drives toward CTAs.', 'mxchat'); ?></span>
691 </button>
692 <button type="button" class="mxch-wiz-behavior-example" data-mxch-example="support">
693 <span class="mxch-wiz-behavior-example-title"><?php esc_html_e('Friendly customer support agent', 'mxchat'); ?></span>
694 <span class="mxch-wiz-behavior-example-desc"><?php esc_html_e('Patient, empathetic, troubleshoots, escalates when stuck.', 'mxchat'); ?></span>
695 </button>
696 </div>
697
698 <div class="mxch-wiz-error" data-mxch-which="behavior" hidden></div>
699
700 <div class="mxch-wizard-actions">
701 <button type="button" class="mxch-btn mxch-btn-secondary mxch-wiz-back" data-mxch-from-step="2"><?php esc_html_e('Back', 'mxchat'); ?></button>
702 <button type="button" class="mxch-btn mxch-btn-primary mxch-wiz-continue" data-mxch-from-step="2"><?php esc_html_e('Continue', 'mxchat'); ?></button>
703 </div>
704 </section>
705
706 <!-- STEP 3 embedding provider + model + (maybe) key -->
707 <section class="mxch-wizard-step" data-step="3" hidden>
708 <h2 class="mxch-wizard-step-title"><?php esc_html_e('Choose your embedding model', 'mxchat'); ?></h2>
709 <p class="mxch-wizard-step-explainer">
710 <?php esc_html_e('Embeddings turn your website content and knowledge base into a format MxChat can search through — this is what makes your chatbot "know" your stuff. You only need this if you\'ll use a knowledge base, but most users do.', 'mxchat'); ?>
711 </p>
712
713 <div class="mxch-field">
714 <label class="mxch-field-label" for="mxch-wiz-embed-provider"><?php esc_html_e('Provider', 'mxchat'); ?></label>
715 <select class="mxch-wiz-select" id="mxch-wiz-embed-provider" data-mxch-which="embedding" data-mxch-role="provider">
716 <option value=""><?php echo esc_html($wizard_config['strings']['selectProvider']); ?></option>
717 <?php foreach ($catalog as $slug => $entry):
718 if (empty($entry['embedding_models'])) continue; ?>
719 <option value="<?php echo esc_attr($slug); ?>" <?php selected($current_embed_provider, $slug); ?>><?php echo esc_html($entry['label']); ?></option>
720 <?php endforeach; ?>
721 </select>
722 </div>
723
724 <div class="mxch-field mxch-wiz-model-field" data-mxch-which="embedding" hidden>
725 <label class="mxch-field-label" for="mxch-wiz-embed-model"><?php esc_html_e('Model', 'mxchat'); ?></label>
726 <select class="mxch-wiz-select" id="mxch-wiz-embed-model" data-mxch-which="embedding" data-mxch-role="model"></select>
727 </div>
728
729 <!-- Dedup case: same provider as Step 1 + key already saved -->
730 <div class="mxch-wiz-key-dedup" data-mxch-which="embedding" hidden>
731 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
732 <span data-mxch-dedup-text></span>
733 </div>
734
735 <div class="mxch-field mxch-wiz-key-field" data-mxch-which="embedding" hidden>
736 <label class="mxch-field-label" data-mxch-key-label></label>
737 <p class="mxch-field-description"><?php esc_html_e('An API key is a password from your AI provider that lets MxChat talk to them.', 'mxchat'); ?></p>
738 <div class="mxch-wiz-key-row">
739 <input type="password" class="mxch-wiz-key-input" data-mxch-which="embedding" autocomplete="new-password" data-lpignore="true" />
740 <button type="button" class="mxch-btn mxch-btn-primary mxch-btn-sm mxch-wiz-key-save" data-mxch-which="embedding"><?php echo esc_html($wizard_config['strings']['saveKey']); ?></button>
741 </div>
742 <p class="mxch-field-hint"><?php echo wp_kses_post(sprintf(
743 __('You can also manage all your keys later in %s.', 'mxchat'),
744 '<a href="' . esc_url($wizard_config['urls']['apiKeys']) . '">' . esc_html__('API Keys settings', 'mxchat') . '</a>'
745 )); ?></p>
746 </div>
747
748 <div class="mxch-wiz-key-saved" data-mxch-which="embedding" hidden>
749 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
750 <span data-mxch-key-saved-text></span>
751 <button type="button" class="mxch-wiz-replace-key-link" data-mxch-which="embedding"><?php echo esc_html($wizard_config['strings']['replaceKey']); ?></button>
752 </div>
753
754 <div class="mxch-wiz-error" data-mxch-which="embedding" hidden></div>
755
756 <!-- Vector-store informational note (plan-23987f). Pinecone and
757 OpenAI Vector Store are alternative *databases* (not embedding
758 models) configured under Knowledge after onboarding. -->
759 <div class="mxch-wiz-vector-store-note">
760 <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" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
761 <span><?php echo wp_kses_post(sprintf(
762 /* translators: %s = link to Knowledge page */
763 __('By default MxChat stores embeddings in your WordPress database. After onboarding you can switch to %s for larger knowledge bases.', 'mxchat'),
764 '<a href="' . esc_url($wizard_config['urls']['kb']) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Pinecone or OpenAI Vector Store', 'mxchat') . '</a>'
765 )); ?></span>
766 </div>
767
768 <div class="mxch-wizard-actions">
769 <button type="button" class="mxch-btn mxch-btn-secondary mxch-wiz-back" data-mxch-from-step="3"><?php esc_html_e('Back', 'mxchat'); ?></button>
770 <button type="button" class="mxch-btn mxch-btn-primary mxch-wiz-continue" data-mxch-from-step="3" disabled><?php esc_html_e('Continue', 'mxchat'); ?></button>
771 </div>
772 </section>
773
774 <!-- STEP 4 KB seed (optional, mirrors Actions) -->
775 <section class="mxch-wizard-step" data-step="4" hidden>
776 <h2 class="mxch-wizard-step-title"><?php esc_html_e('Add something to your knowledge base', 'mxchat'); ?></h2>
777 <p class="mxch-wizard-step-explainer">
778 <?php esc_html_e('Your knowledge base is the content MxChat uses to answer questions about your site — pages, posts, PDFs, or custom text. Add some now if you\'d like, or skip this and build it later from the Knowledge page.', 'mxchat'); ?>
779 </p>
780
781 <div class="mxch-wiz-cta-row">
782 <a class="mxch-btn mxch-btn-primary" href="<?php echo esc_url($wizard_config['urls']['kb']); ?>" target="_blank" rel="noopener noreferrer">
783 <?php esc_html_e('Open Knowledge Base', 'mxchat'); ?>
784 <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
785 </a>
786 </div>
787
788 <div class="mxch-wizard-actions">
789 <button type="button" class="mxch-btn mxch-btn-secondary mxch-wiz-back" data-mxch-from-step="4"><?php esc_html_e('Back', 'mxchat'); ?></button>
790 <button type="button" class="mxch-btn mxch-btn-secondary mxch-wiz-continue" data-mxch-from-step="4"><?php esc_html_e('I\'ll skip this for now — Continue', 'mxchat'); ?></button>
791 </div>
792 </section>
793
794 <!-- STEP 5 Actions (optional) -->
795 <section class="mxch-wizard-step" data-step="5" hidden>
796 <h2 class="mxch-wizard-step-title"><?php esc_html_e('Try MxChat Actions', 'mxchat'); ?></h2>
797 <p class="mxch-wizard-step-explainer">
798 <?php esc_html_e('Actions let your chatbot do things, not just answer. When a visitor\'s message matches phrases you choose, MxChat can capture their email to a Loops list, search the web and images with Brave, generate an image with OpenAI or Gemini, answer questions about a PDF you\'ve uploaded, or hand the conversation off to a human agent on Slack or Telegram. This step is optional — skip it now and set up Actions any time.', 'mxchat'); ?>
799 </p>
800
801 <div class="mxch-wiz-cta-row">
802 <a class="mxch-btn mxch-btn-primary" href="<?php echo esc_url($wizard_config['urls']['actions']); ?>" target="_blank" rel="noopener noreferrer">
803 <?php esc_html_e('Open Actions', 'mxchat'); ?>
804 <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
805 </a>
806 </div>
807
808 <div class="mxch-wizard-actions">
809 <button type="button" class="mxch-btn mxch-btn-secondary mxch-wiz-back" data-mxch-from-step="5"><?php esc_html_e('Back', 'mxchat'); ?></button>
810 <button type="button" class="mxch-btn mxch-btn-secondary mxch-wiz-continue" data-mxch-from-step="5"><?php esc_html_e('I\'ll skip this for now — Continue', 'mxchat'); ?></button>
811 </div>
812 </section>
813
814 <!-- STEP 6 Congrats (plan-23987f rebuild: 3 next-step cards) -->
815 <section class="mxch-wizard-step mxch-wizard-step-congrats" data-step="6" hidden>
816 <div class="mxch-wiz-congrats-icon">
817 <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" 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>
818 </div>
819 <h2 class="mxch-wizard-step-title mxch-wizard-step-title-congrats">🎉 <?php esc_html_e('You\'re set up.', 'mxchat'); ?></h2>
820
821 <div class="mxch-wiz-nextsteps-grid">
822
823 <!-- Card 1 Test on Settings Testing tab -->
824 <a class="mxch-wiz-nextstep-card" href="<?php echo esc_url($wizard_config['urls']['settingsTesting']); ?>">
825 <div class="mxch-wiz-nextstep-icon">
826 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" 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>
827 </div>
828 <h3 class="mxch-wiz-nextstep-title"><?php esc_html_e('Test your chatbot', 'mxchat'); ?></h3>
829 <p class="mxch-wiz-nextstep-desc"><?php esc_html_e('Try it right inside your dashboard — no need to visit your site.', 'mxchat'); ?></p>
830 <span class="mxch-wiz-nextstep-cta"><?php esc_html_e('Open Testing tab', 'mxchat'); ?>
831 <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"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
832 </span>
833 </a>
834
835 <!-- Card 2 Show it on the site -->
836 <a class="mxch-wiz-nextstep-card" href="<?php echo esc_url($wizard_config['urls']['settingsDisplay']); ?>">
837 <div class="mxch-wiz-nextstep-icon">
838 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
839 </div>
840 <h3 class="mxch-wiz-nextstep-title"><?php esc_html_e('Show it on your site', 'mxchat'); ?></h3>
841 <p class="mxch-wiz-nextstep-desc"><?php esc_html_e('Turn the chatbot on for all pages, or hide it — your choice.', 'mxchat'); ?></p>
842 <span class="mxch-wiz-nextstep-cta"><?php esc_html_e('Open Display settings', 'mxchat'); ?>
843 <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"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
844 </span>
845 </a>
846
847 <!-- Card 3 Embed code shortcodes -->
848 <div class="mxch-wiz-nextstep-card mxch-wiz-nextstep-card-embed">
849 <div class="mxch-wiz-nextstep-icon">
850 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
851 </div>
852 <h3 class="mxch-wiz-nextstep-title"><?php esc_html_e('Or embed it manually', 'mxchat'); ?></h3>
853 <p class="mxch-wiz-nextstep-desc"><?php esc_html_e('Prefer to place it yourself? Paste a shortcode anywhere.', 'mxchat'); ?></p>
854
855 <div class="mxch-wiz-shortcode-row">
856 <code class="mxch-wiz-shortcode" data-mxch-shortcode='[mxchat_chatbot floating="yes"]'>[mxchat_chatbot floating="yes"]</code>
857 <button type="button" class="mxch-wiz-shortcode-copy" data-mxch-copy-shortcode='[mxchat_chatbot floating="yes"]' aria-label="<?php esc_attr_e('Copy floating widget shortcode', 'mxchat'); ?>">
858 <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"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
859 <span class="mxch-wiz-shortcode-copy-text"><?php esc_html_e('Copy', 'mxchat'); ?></span>
860 </button>
861 </div>
862 <p class="mxch-wiz-shortcode-caption"><?php esc_html_e('Floating widget (bottom corner)', 'mxchat'); ?></p>
863
864 <div class="mxch-wiz-shortcode-row">
865 <code class="mxch-wiz-shortcode" data-mxch-shortcode='[mxchat_chatbot floating="no"]'>[mxchat_chatbot floating="no"]</code>
866 <button type="button" class="mxch-wiz-shortcode-copy" data-mxch-copy-shortcode='[mxchat_chatbot floating="no"]' aria-label="<?php esc_attr_e('Copy inline widget shortcode', 'mxchat'); ?>">
867 <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"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
868 <span class="mxch-wiz-shortcode-copy-text"><?php esc_html_e('Copy', 'mxchat'); ?></span>
869 </button>
870 </div>
871 <p class="mxch-wiz-shortcode-caption"><?php esc_html_e('Inline embedded chat', 'mxchat'); ?></p>
872 </div>
873
874 </div>
875
876 <!-- PRO add-ons conversion block (plan-9451be) -->
877 <a class="mxch-wiz-pro-promo" href="<?php echo esc_url($wizard_config['urls']['proAddons']); ?>" target="_blank" rel="noopener noreferrer">
878 <span class="mxch-wiz-pro-promo-badge"><?php esc_html_e('PRO add-ons', 'mxchat'); ?></span>
879 <div class="mxch-wiz-pro-promo-body">
880 <h3 class="mxch-wiz-pro-promo-title"><?php esc_html_e('Do more with PRO add-ons', 'mxchat'); ?></h3>
881 <p class="mxch-wiz-pro-promo-desc">
882 <?php esc_html_e('Sell directly in chat with WooCommerce, run multiple specialized bots on one site, generate full SEO-ready posts with Advanced Content, and more.', 'mxchat'); ?>
883 </p>
884 </div>
885 <span class="mxch-wiz-pro-promo-cta">
886 <?php esc_html_e('Explore PRO add-ons', 'mxchat'); ?>
887 <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"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
888 </span>
889 </a>
890
891 <p class="mxch-wiz-congrats-graduated" id="mxch-wiz-graduated-line">
892 <?php
893 $mxch_wiz_tutorials_url = esc_url(admin_url('admin.php?page=mxchat-settings#tutorials'));
894 printf(
895 /* translators: %s: bold link to "Settings → Tutorials" */
896 wp_kses(
897 __('This setup guide moves out of the way once you\'re done — reopen it any time under %s.', 'mxchat'),
898 array('strong' => array(), 'a' => array('href' => array()))
899 ),
900 '<strong><a href="' . $mxch_wiz_tutorials_url . '">' . esc_html__('Settings → Tutorials', 'mxchat') . '</a></strong>'
901 );
902 ?>
903 </p>
904
905 <div class="mxch-wiz-congrats-footer">
906 <button type="button" class="mxch-wiz-congrats-footer-link mxch-wiz-back" data-mxch-from-step="6"><?php esc_html_e('Review earlier steps', 'mxchat'); ?></button>
907 <a class="mxch-wiz-congrats-footer-link" href="<?php echo esc_url($wizard_config['urls']['homepage']); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open my live site', 'mxchat'); ?></a>
908 </div>
909 </section>
910
911 </div>
912
913 <!-- Need help? escape hatch (preserved from f7c7d4) -->
914 <div class="mxch-card mxch-onboarding-help" id="mxch-onboarding-help">
915 <div class="mxch-card-body">
916 <div class="mxch-help-row">
917 <div>
918 <h3 class="mxch-help-title"><?php esc_html_e('Stuck? Get help.', 'mxchat'); ?></h3>
919 <p class="mxch-help-subtitle"><?php esc_html_e('Ask the docs bot for an answer, or open a ticket if something\'s broken.', 'mxchat'); ?></p>
920 </div>
921 <div class="mxch-help-buttons">
922 <a class="mxch-btn mxch-btn-secondary mxch-btn-sm" href="https://mxchat.ai/documentation-bot/" target="_blank" rel="noopener noreferrer">
923 <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="margin-right:6px;"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>
924 <?php esc_html_e('Ask the docs bot', 'mxchat'); ?>
925 </a>
926 <a class="mxch-btn mxch-btn-secondary mxch-btn-sm" href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer">
927 <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="margin-right:6px;"><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>
928 <?php esc_html_e('Open a support ticket', 'mxchat'); ?>
929 </a>
930 </div>
931 </div>
932 </div>
933 </div>
934
935 <!-- Dismiss onboarding bottom of page, ghost-styled, low visual weight
936 so users don't tap it by accident. Plan-d14e89 Issue 7. -->
937 <p class="mxch-onboarding-dismiss-row">
938 <button type="button"
939 class="mxch-onboarding-dismiss-link mxch-onboarding-dismiss"
940 data-mxch-dismiss-nonce="<?php echo esc_attr($dismiss_nonce); ?>"
941 aria-describedby="mxch-onboarding-dismiss-hint">
942 <?php esc_html_e('Dismiss onboarding', 'mxchat'); ?>
943 </button>
944 <span class="mxch-onboarding-dismiss-hint" id="mxch-onboarding-dismiss-hint">
945 <?php esc_html_e('Hide the setup guide from the menu reopen it any time under Settings Tutorials.', 'mxchat'); ?>
946 </span>
947 </p>
948 </div>
949
950 </main>
951 </div>
952
953 <?php
954 // Render the Sample Instructions modal markup once at the end of the page,
955 // matching the Settings modal exactly (same DOM ids → same admin-style.css
956 // styling + same UX expectations). admin-onboarding-wizard.js wires the
957 // show/hide handlers locally so we don't need to enqueue the heavy
958 // mxchat-admin.js on this page just for the modal.
959 // Content kept verbatim with class-mxchat-admin.php::render_sample_instructions_modal()
960 // so users see the same sample no matter where they invoked the modal from.
961 ?>
962 <div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">
963 <div class="mxchat-instructions-modal-content">
964 <div class="mxchat-instructions-modal-header">
965 <h3 class="mxchat-instructions-modal-title">
966 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
967 <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
968 <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
969 </svg>
970 <?php esc_html_e('Sample AI Instructions', 'mxchat'); ?>
971 </h3>
972 <button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">
973 <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">
974 <line x1="18" y1="6" x2="6" y2="18"/>
975 <line x1="6" y1="6" x2="18" y2="18"/>
976 </svg>
977 </button>
978 </div>
979 <div class="mxchat-instructions-modal-body">
980 <div class="mxchat-instructions-content"><?php echo esc_html('You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
981
982 # Response Style - CRITICALLY IMPORTANT
983 - MAXIMUM LENGTH: 1-3 short sentences per response
984 - Ultra-concise: Get straight to the answer with no filler
985 - No introductions like "Sure!" or "I\'d be happy to help"
986 - No phrases like "based on my knowledge" or "according to information"
987 - No explanatory text before giving the answer
988 - No summaries or repetition
989 - Hyperlink all URLs
990 - Respond in user\'s language
991 - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
992
993 # Knowledge Base Requirements - PREVENT HALLUCINATIONS
994 - ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
995 - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
996 - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
997 - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
998 - Better to admit insufficient information than provide inaccurate answers'); ?></div>
999 <button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">
1000 <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">
1001 <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
1002 <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
1003 </svg>
1004 <?php esc_html_e('Copy to clipboard', 'mxchat'); ?>
1005 </button>
1006 </div>
1007 </div>
1008 </div>
1009
1010 <script>
1011 window.MxChatOnboardingWizard = <?php echo wp_json_encode($wizard_config); ?>;
1012 </script>
1013 <?php
1014 }
1015