Changeset 3445725
- Timestamp:
- 01/23/2026 04:49:32 PM (4 weeks ago)
- Location:
- convertybot
- Files:
-
- 97 added
- 24 edited
-
tags/1.0.28 (added)
-
tags/1.0.28/LICENSE.txt (added)
-
tags/1.0.28/assets (added)
-
tags/1.0.28/assets/css (added)
-
tags/1.0.28/assets/css/admin-analytics.css (added)
-
tags/1.0.28/assets/css/admin-configuration.css (added)
-
tags/1.0.28/assets/css/admin-conversations.css (added)
-
tags/1.0.28/assets/css/admin-coupons.css (added)
-
tags/1.0.28/assets/css/admin-subscription.css (added)
-
tags/1.0.28/assets/css/admin-visitor-journeys.css (added)
-
tags/1.0.28/assets/css/admin.css (added)
-
tags/1.0.28/assets/css/analytics-comprehensive.css (added)
-
tags/1.0.28/assets/css/analytics-redesigned.css (added)
-
tags/1.0.28/assets/css/chat-modal-fixes.css (added)
-
tags/1.0.28/assets/css/chatbot-improvements.css (added)
-
tags/1.0.28/assets/css/consent-banner.css (added)
-
tags/1.0.28/assets/css/frontend-enhanced.css (added)
-
tags/1.0.28/assets/css/frontend-fixes.css (added)
-
tags/1.0.28/assets/css/frontend-modern.css (added)
-
tags/1.0.28/assets/css/frontend-widget-a11y.css (added)
-
tags/1.0.28/assets/css/frontend.css (added)
-
tags/1.0.28/assets/css/product-cards-inline.css (added)
-
tags/1.0.28/assets/css/products-modal-improved.css (added)
-
tags/1.0.28/assets/css/products-modal.css (added)
-
tags/1.0.28/assets/js (added)
-
tags/1.0.28/assets/js/admin-analytics-dashboard.js (added)
-
tags/1.0.28/assets/js/admin-configuration.js (added)
-
tags/1.0.28/assets/js/admin-conversations.js (added)
-
tags/1.0.28/assets/js/admin-coupons.js (added)
-
tags/1.0.28/assets/js/admin-main.js (added)
-
tags/1.0.28/assets/js/admin-setup-wizard.js (added)
-
tags/1.0.28/assets/js/admin-subscription.js (added)
-
tags/1.0.28/assets/js/admin-visitor-journeys.js (added)
-
tags/1.0.28/assets/js/admin.js (added)
-
tags/1.0.28/assets/js/analytics-comprehensive.js (added)
-
tags/1.0.28/assets/js/analytics-enhanced.js (added)
-
tags/1.0.28/assets/js/analytics-export.js (added)
-
tags/1.0.28/assets/js/analytics-predictive.js (added)
-
tags/1.0.28/assets/js/analytics-redesigned.js (added)
-
tags/1.0.28/assets/js/analytics-segmentation.js (added)
-
tags/1.0.28/assets/js/analytics-websocket.js (added)
-
tags/1.0.28/assets/js/analytics-widgets.js (added)
-
tags/1.0.28/assets/js/chatbot-sounds.js (added)
-
tags/1.0.28/assets/js/consent-banner.js (added)
-
tags/1.0.28/assets/js/coupon-system-enhanced.js (added)
-
tags/1.0.28/assets/js/engagement-tracking-sdk.js (added)
-
tags/1.0.28/assets/js/frontend-enhanced.js (added)
-
tags/1.0.28/assets/js/frontend-fixes.js (added)
-
tags/1.0.28/assets/js/frontend.js (added)
-
tags/1.0.28/assets/js/guest-id-cookie.js (added)
-
tags/1.0.28/assets/js/migration-handler.js (added)
-
tags/1.0.28/assets/js/product-cards-handler.js (added)
-
tags/1.0.28/assets/js/product-showcase-enhanced.js (added)
-
tags/1.0.28/assets/js/service-worker.js (added)
-
tags/1.0.28/assets/js/tracking.js (added)
-
tags/1.0.28/assets/js/vendor (added)
-
tags/1.0.28/assets/js/vendor/chart.min.js (added)
-
tags/1.0.28/assets/js/vendor/handlebars.min.js (added)
-
tags/1.0.28/assets/sounds (added)
-
tags/1.0.28/assets/sounds/README.txt (added)
-
tags/1.0.28/assets/sounds/connect.mp3 (added)
-
tags/1.0.28/assets/sounds/error.mp3 (added)
-
tags/1.0.28/assets/sounds/message.mp3 (added)
-
tags/1.0.28/assets/sounds/notification.mp3 (added)
-
tags/1.0.28/assets/sounds/open.mp3 (added)
-
tags/1.0.28/assets/sounds/send.mp3 (added)
-
tags/1.0.28/convertybot.php (added)
-
tags/1.0.28/includes (added)
-
tags/1.0.28/includes/class-admin.php (added)
-
tags/1.0.28/includes/class-analytics.php (added)
-
tags/1.0.28/includes/class-api.php (added)
-
tags/1.0.28/includes/class-consent-banner.php (added)
-
tags/1.0.28/includes/class-coupon-manager.php (added)
-
tags/1.0.28/includes/class-database.php (added)
-
tags/1.0.28/includes/class-frontend.php (added)
-
tags/1.0.28/includes/class-product-sync.php (added)
-
tags/1.0.28/includes/class-user-profile.php (added)
-
tags/1.0.28/includes/class-user-tracking-enhanced.php (added)
-
tags/1.0.28/includes/class-user-tracking.php (added)
-
tags/1.0.28/languages (added)
-
tags/1.0.28/readme.txt (added)
-
tags/1.0.28/templates (added)
-
tags/1.0.28/templates/admin (added)
-
tags/1.0.28/templates/admin/analytics-comprehensive.php (added)
-
tags/1.0.28/templates/admin/analytics.php (added)
-
tags/1.0.28/templates/admin/configuration-debug.php (added)
-
tags/1.0.28/templates/admin/configuration.php (added)
-
tags/1.0.28/templates/admin/conversations.php (added)
-
tags/1.0.28/templates/admin/coupons.php (added)
-
tags/1.0.28/templates/admin/main.php (added)
-
tags/1.0.28/templates/admin/setup-wizard.php (added)
-
tags/1.0.28/templates/admin/subscription.php (added)
-
tags/1.0.28/templates/admin/visitor-journeys.php (added)
-
tags/1.0.28/templates/frontend (added)
-
tags/1.0.28/templates/frontend/chatbot-widget-enhanced.php (added)
-
tags/1.0.28/templates/frontend/chatbot-widget.php (added)
-
tags/1.0.28/templates/offline.html (added)
-
trunk/assets/js/admin-analytics-dashboard.js (modified) (25 diffs)
-
trunk/assets/js/admin-conversations.js (modified) (40 diffs)
-
trunk/assets/js/admin-coupons.js (modified) (5 diffs)
-
trunk/assets/js/admin-subscription.js (modified) (4 diffs)
-
trunk/assets/js/admin-visitor-journeys.js (modified) (18 diffs)
-
trunk/assets/js/admin.js (modified) (3 diffs)
-
trunk/assets/js/analytics-comprehensive.js (modified) (9 diffs)
-
trunk/assets/js/analytics-enhanced.js (modified) (8 diffs)
-
trunk/assets/js/analytics-redesigned.js (modified) (21 diffs)
-
trunk/assets/js/analytics-websocket.js (modified) (18 diffs)
-
trunk/assets/js/chatbot-sounds.js (modified) (1 diff)
-
trunk/assets/js/consent-banner.js (modified) (14 diffs)
-
trunk/assets/js/coupon-system-enhanced.js (modified) (7 diffs)
-
trunk/assets/js/engagement-tracking-sdk.js (modified) (22 diffs)
-
trunk/assets/js/frontend-enhanced.js (modified) (32 diffs)
-
trunk/assets/js/frontend.js (modified) (46 diffs)
-
trunk/assets/js/guest-id-cookie.js (modified) (7 diffs)
-
trunk/assets/js/migration-handler.js (modified) (12 diffs)
-
trunk/assets/js/product-cards-handler.js (modified) (1 diff)
-
trunk/assets/js/product-showcase-enhanced.js (modified) (2 diffs)
-
trunk/assets/js/service-worker.js (modified) (22 diffs)
-
trunk/assets/js/tracking.js (modified) (4 diffs)
-
trunk/convertybot.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
convertybot/trunk/assets/js/admin-analytics-dashboard.js
r3437100 r3445725 31 31 this.initializeCharts(); 32 32 this.updateLastUpdateTime(); 33 34 // DEBUG: Log all dashboard data for charts35 console.log('═══════════════════════════════════════════════════════════');36 console.log('📊 Enhanced Analytics Dashboard initialized');37 console.log('═══════════════════════════════════════════════════════════');38 console.log('📊 [FULL DATA] All dashboard data received from backend:', this.config.dashboardData);39 console.log('📊 [Session Stats]:', this.config.dashboardData?.session_stats);40 console.log('📊 [Comparison Data]:', this.config.dashboardData?.comparison_data);41 console.log('📊 [Hourly Pattern] Records:', this.config.dashboardData?.hourly_pattern?.length || 0);42 console.log('📊 [Hourly Pattern] Sample:', this.config.dashboardData?.hourly_pattern?.slice(0, 3));43 console.log('📊 [Conversion Funnel]:', this.config.dashboardData?.conversion_funnel);44 console.log('📊 [Device Breakdown]:', this.config.dashboardData?.device_breakdown);45 console.log('📊 [Top Products] Count:', this.config.dashboardData?.top_products?.length || 0);46 console.log('═══════════════════════════════════════════════════════════');47 33 } 48 34 … … 139 125 initializeCharts() { 140 126 if (typeof Chart === 'undefined') { 141 console.warn('Chart.js not loaded');142 127 return; 143 128 } … … 159 144 this.initHeatmapChart(); 160 145 this.initMiniTrendCharts(); 161 console.log('Charts initialized successfully');162 146 } catch (error) { 163 147 console.error('Error initializing charts:', error); … … 174 158 delete this.charts[key]; 175 159 } catch (error) { 176 console.warn(`Failed to destroy chart ${key}:`, error);160 // Chart destruction failed - continue anyway 177 161 } 178 162 } … … 271 255 // Get real data from backend 272 256 const hourlyMetrics = this.config.dashboardData.hourly_pattern || []; 273 274 // DEBUG: Log raw data from backend275 console.log('📊 [Revenue Chart] Raw hourly_pattern data:', {276 totalRecords: hourlyMetrics.length,277 sampleData: hourlyMetrics.slice(0, 5),278 allData: hourlyMetrics,279 currentTimeframe: this.currentTimeframe280 });281 257 282 258 // Aggregate data based on current timeframe … … 318 294 }); 319 295 320 // DEBUG: Log aggregated data with actual values321 console.log('📊 [Revenue Chart] Aggregated data:', {322 timeframe: this.currentTimeframe,323 labels: labels,324 revenueDataValues: JSON.stringify(revenueData),325 conversionDataValues: JSON.stringify(conversionData),326 aggregatedRecords: aggregatedData.length,327 totalRevenue: revenueData.reduce((a, b) => a + b, 0),328 totalSessions: aggregatedData.reduce((a, b) => a + (b.sessions || 0), 0),329 rawAggregated: JSON.stringify(aggregatedData)330 });331 332 296 // Destroy existing revenue chart if exists 333 297 if (this.charts.revenue) { 334 console.log('📊 [Revenue Chart] Destroying existing chart');335 298 this.charts.revenue.destroy(); 336 299 this.charts.revenue = null; … … 338 301 339 302 setTimeout(() => { 340 console.log('📊 [Revenue Chart] Creating new chart with', revenueData.length, 'data points');341 342 303 // Get sessions data - this is now the default 343 304 const sessionsData = aggregatedData.map(d => d.sessions || 0); 344 const hasRevenue = revenueData.some(v => v > 0);345 const hasSessions = sessionsData.some(v => v > 0);346 347 console.log('📊 [Revenue Chart] Data check:', {348 hasRevenue,349 hasSessions,350 revenueSum: revenueData.reduce((a, b) => a + b, 0),351 sessionsSum: sessionsData.reduce((a, b) => a + b, 0)352 });353 305 354 306 // Use sessions data as primary (default selection) … … 475 427 const canvas = document.getElementById('funnelChart'); 476 428 if (!canvas) { 477 console.warn('Funnel chart canvas not found');478 429 return; 479 430 } … … 482 433 // Get real conversion funnel data from backend 483 434 const funnel = this.config.dashboardData.conversion_funnel || {}; 484 485 // DEBUG: Log funnel data486 console.log('📊 [Funnel Chart] Raw conversion_funnel data:', {487 rawFunnel: funnel,488 sessions: funnel.sessions,489 chat_initiated: funnel.chat_initiated,490 products_viewed: funnel.products_viewed,491 add_to_cart: funnel.add_to_cart,492 purchases: funnel.purchases493 });494 435 495 436 const data = { … … 503 444 ] 504 445 }; 505 506 console.log('📊 [Funnel Chart] Processed values:', data.values);507 446 508 447 this.charts.funnel = new Chart(canvas, { … … 558 497 const canvas = document.getElementById('aiAccuracyGauge'); 559 498 if (!canvas) { 560 console.warn('AI Gauge chart canvas not found');561 499 return; 562 500 } … … 600 538 const canvas = document.getElementById('deviceChart'); 601 539 if (!canvas) { 602 console.warn('Device chart canvas not found');603 540 return; 604 541 } … … 651 588 const canvas = document.getElementById('activityHeatmap'); 652 589 if (!canvas) { 653 console.warn('Heatmap chart canvas not found');654 590 return; 655 591 } … … 660 596 const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`); 661 597 const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 662 663 // DEBUG: Log heatmap data664 console.log('📊 [Heatmap Chart] Raw hourly_pattern data:', {665 totalRecords: hourlyMetrics.length,666 sampleData: hourlyMetrics.slice(0, 10),667 hasHourField: hourlyMetrics.length > 0 ? hourlyMetrics[0].hour !== undefined : false,668 hasDayField: hourlyMetrics.length > 0 ? hourlyMetrics[0].day !== undefined : false669 });670 598 671 599 // Group hourly metrics by day of week and hour … … 818 746 } 819 747 }, 4000); 820 821 console.log(`${type.toUpperCase()}: ${message}`);822 748 } 823 749 … … 844 770 this.loadDashboardData().then(() => { 845 771 this.showNotification('Dashboard refreshed successfully', 'success'); 846 }).catch((error) => { 847 console.error('Failed to refresh dashboard:', error); 772 }).catch(() => { 848 773 this.showNotification('Refreshing page...', 'info'); 849 774 // Fallback to page reload if AJAX fails … … 876 801 } 877 802 } catch (error) { 878 console.warn('API request failed, using fallback data:', error);879 803 // Use mock data as fallback 880 804 this.updateLastUpdateTime(); … … 909 833 return result; 910 834 } catch (error) { 911 console.error(`API request failed for ${action}:`, error);912 835 throw error; 913 836 } … … 1042 965 1043 966 updateChartTimeframe(timeframe) { 1044 console.log('📊 [Timeframe Change] Switching from', this.currentTimeframe, 'to', timeframe);1045 1046 967 // Update active button 1047 968 document.querySelectorAll('.chart-control').forEach(btn => { … … 1053 974 1054 975 // Reinitialize charts with new timeframe 1055 console.log('📊 [Timeframe Change] Reinitializing charts...');1056 976 this.initializeCharts(); 1057 977 … … 1060 980 1061 981 updateRevenueChart(metric) { 1062 console.log('📊 [Revenue Chart] Switching metric to:', metric);1063 1064 982 // Update the revenue chart to show different metrics 1065 983 if (!this.charts.revenue) { 1066 console.warn('Revenue chart not initialized');1067 984 return; 1068 985 } … … 1110 1027 } 1111 1028 1112 console.log('📊 [Revenue Chart] New data for', metric, ':', {1113 dataLength: newData.length,1114 values: JSON.stringify(newData),1115 sum: newData.reduce((a, b) => a + b, 0)1116 });1117 1118 1029 // Update the first dataset (revenue/main metric) 1119 1030 this.charts.revenue.data.datasets[0].data = newData.length > 0 ? newData : [0]; … … 1124 1035 1125 1036 this.charts.revenue.update('active'); 1126 console.log('📊 [Revenue Chart] Updated to show:', label);1127 1037 } 1128 1038 … … 1139 1049 delete this.charts.heatmap; 1140 1050 } catch (error) { 1141 console.warn('Failed to destroy heatmap chart:', error);1051 // Heatmap chart destruction failed - continue anyway 1142 1052 } 1143 1053 } … … 1568 1478 toggleFullscreen() { 1569 1479 if (!document.fullscreenElement) { 1570 document.documentElement.requestFullscreen?.().catch(err => { 1571 console.error('Error attempting to enable fullscreen:', err); 1480 document.documentElement.requestFullscreen?.().catch(() => { 1572 1481 this.showNotification('Fullscreen not supported', 'warning'); 1573 1482 }); -
convertybot/trunk/assets/js/admin-conversations.js
r3437100 r3445725 31 31 // Initialize the conversation viewer 32 32 init: function() { 33 console.log('ConvertyBot Conversations starting initialization...');34 35 33 // Merge configuration from template 36 34 if (window.convertyBotConversationsConfig) { 37 console.log('Merging configuration from template:', window.convertyBotConversationsConfig);38 35 // Merge the config object with template config 39 36 Object.assign(this, window.convertyBotConversationsConfig); 40 37 } 41 42 console.log('Final configuration:', this); 43 38 44 39 // Verify required properties 45 40 if (!this.ajaxurl || !this.nonce) { … … 48 43 return; 49 44 } 50 45 51 46 this.compileTemplates(); 52 47 this.bindEvents(); … … 54 49 this.initRealTime(); 55 50 this.setupAutoRefresh(); 56 57 console.log('ConvertyBot Conversations initialized');58 51 }, 59 52 … … 135 128 // Debug helper to log data 136 129 Handlebars.registerHelper('debug', function(data) { 137 console.log('Handlebars debug:', data);130 // Debug logging removed for production 138 131 return ''; 139 132 }); … … 354 347 e.preventDefault(); 355 348 var page = parseInt($(this).data('page')); 356 console.log('📄 Pagination click:', {357 clickedPage: page,358 currentPage: self.config.currentPage,359 element: $(this).text()360 });361 349 if (page && page !== self.config.currentPage) { 362 console.log('📄 Loading page:', page);363 350 self.config.currentPage = page; 364 351 self.loadConversations(); 365 } else {366 console.log('📄 Same page or invalid, not loading');367 352 } 368 353 }); … … 372 357 loadConversations: function() { 373 358 var self = this; 374 375 console.log('loadConversations called'); 376 console.log('Configuration check:', { 377 hasConfig: !!convertyBotConversations, 378 hasAjaxUrl: !!(convertyBotConversations && convertyBotConversations.ajaxurl), 379 hasNonce: !!(convertyBotConversations && convertyBotConversations.nonce), 380 config: convertyBotConversations, 381 windowVars: Object.keys(window).filter(key => key.includes('ConvertyBot') || key.includes('convertyBot')) 382 }); 383 359 384 360 // Check if essential configuration is available 385 361 if (!convertyBotConversations || !convertyBotConversations.ajaxurl || !convertyBotConversations.nonce) { 386 console.error('Configuration missing!', convertyBotConversations); 387 console.error('Available window variables:', Object.keys(window).filter(key => key.toLowerCase().includes('convertybot'))); 362 console.error('Configuration missing - ajaxurl or nonce not available'); 388 363 self.showError('Configuration error: Missing required AJAX settings. Please check WordPress localization in admin.php'); 389 364 return; … … 410 385 sort: this.config.filters.sort 411 386 }; 412 413 console.log('Making AJAX request with data:', data); 414 387 415 388 $.post(convertyBotConversations.ajaxurl, data) 416 389 .done(function(response) { 417 console.log('🔍 AJAX response:', response);418 console.log('🔍 Response keys:', Object.keys(response));419 390 $('#conversationsList .loading-spinner').hide(); 420 391 421 392 if (response.success) { 422 393 var dataToRender = response.data || response; 423 console.log('🔍 Passing to renderConversations:', dataToRender);424 394 self.renderConversations(dataToRender); 425 395 … … 433 403 }) 434 404 .fail(function(xhr, status, error) { 435 console.error('AJAX request failed:', {436 status: status,437 error: error,438 responseText: xhr.responseText,439 xhr: xhr440 });441 405 $('#conversationsList .loading-spinner').hide(); 442 406 self.showError('AJAX Error: ' + error + ' (Status: ' + status + ')'); … … 449 413 var container = $('#conversationsList'); 450 414 container.empty(); 451 452 console.log('🔍 renderConversations data:', data); 453 console.log('🔍 Data keys:', data ? Object.keys(data) : 'N/A'); 454 415 455 416 var conversations = []; 456 417 if (data && data.conversations) { 457 418 conversations = data.conversations; 458 console.log('✅ Found conversations in data.conversations');459 419 } else if (data && data.data && data.data.conversations) { 460 420 conversations = data.data.conversations; 461 console.log('✅ Found conversations in data.data.conversations');462 421 } else if (Array.isArray(data)) { 463 422 conversations = data; 464 console.log('✅ Data is directly an array'); 465 } else { 466 console.log('❌ No conversations found'); 467 console.log('🔍 Full data:', JSON.stringify(data, null, 2)); 468 } 469 470 console.log('📊 Conversations count:', conversations.length); 471 console.log('📊 Is array:', Array.isArray(conversations)); 423 } 472 424 473 425 if (!Array.isArray(conversations) || conversations.length === 0) { … … 477 429 478 430 conversations.forEach(function(conversation, index) { 479 console.log('🔍 Conversation', index + 1, ':', conversation);480 console.log('📋 Keys:', Object.keys(conversation));481 console.log('📋 Metadata:', conversation.metadata);482 console.log('📋 Tags:', conversation.tags);483 console.log('📋 Notes:', conversation.adminNotes);484 485 431 var tags = []; 486 432 var notes = []; 487 433 488 434 if (conversation.metadata?.tags) { 489 435 tags = conversation.metadata.tags; 490 console.log('✅ Tags from metadata:', tags);491 436 } 492 437 if (conversation.metadata?.adminNotes) { 493 438 notes = conversation.metadata.adminNotes; 494 console.log('✅ Notes from metadata:', notes);495 439 } 496 440 if (conversation.tags && tags.length === 0) { 497 441 tags = conversation.tags; 498 console.log('✅ Tags from root:', tags);499 442 } 500 443 if (conversation.adminNotes && notes.length === 0) { 501 444 notes = conversation.adminNotes; 502 console.log('✅ Notes from root:', notes); 503 } 504 505 console.log('🏷️ Final tags:', tags); 506 console.log('📝 Final notes:', notes); 445 } 507 446 508 447 // CRITICAL FIX: Use pre-computed values from PHP if available, fallback to JS calculation … … 524 463 var statusLabel = conversation.statusLabel || self.getStatusLabel(status); 525 464 526 // Debug: Log what we received from PHP527 console.log('🔍 PHP pre-computed values:', {528 phpTimeAgo: conversation.timeAgo,529 phpStatus: conversation.status,530 phpStatusLabel: conversation.statusLabel,531 finalTimeAgo: timeAgo,532 finalStatus: status,533 finalStatusLabel: statusLabel534 });535 536 465 // ✅ FIX: Extract user info - check ALL possible paths for name AND email 537 466 var userName = conversation.userName || conversation.userInfo?.userName || conversation.user?.name || conversation.user?.userName || null; … … 539 468 var userEmail = conversation.userEmail || conversation.userInfo?.userEmail || conversation.userInfo?.email || conversation.user?.email || conversation.email || null; 540 469 var userId = conversation.userId || null; 541 542 // 🔍 DEBUG: Log extraction543 console.log('🔍 List User Info:', { userName, userEmail, userId, raw: conversation });544 470 545 471 // Build display name: prefer actual name, fallback to email, then userId, then Anonymous … … 582 508 }; 583 509 584 console.log('🎨 Final template data:', templateData);585 586 510 try { 587 511 var html; 588 512 if (self.templates.conversation) { 589 console.log('📝 Using Handlebars template');590 513 html = self.templates.conversation(templateData); 591 514 } else { 592 console.log('📝 Using fallback generateConversationHTML');593 515 html = self.generateConversationHTML(templateData); 594 516 } 595 console.log('📄 Generated HTML preview:', html.substring(0, 500));596 517 container.append(html); 597 console.log('✅ Conversation rendered');598 518 } catch (error) { 599 console.error(' ❌ Render error:', error);519 console.error('Render error for conversation:', error); 600 520 container.append('<div class="conversation-error">Error rendering conversation</div>'); 601 521 } … … 626 546 .done(function(response) { 627 547 $('#conversationViewer .messages-loading').hide(); 628 629 console.log('🌐 Conversation loaded - Messages:', response.data && response.data.messages ? response.data.messages.length : 0); 630 548 631 549 if (response.success) { 632 550 self.renderConversationDetails(response.data); 633 551 } else { 634 console.error('❌ AJAX Response failed:', response);635 552 self.showError((response && response.data && response.data.error) || (convertyBotConversations.strings && convertyBotConversations.strings.error) || 'An error occurred loading conversation'); 636 553 } … … 645 562 renderConversationDetails: function(data) { 646 563 var self = this; 647 console.log('🔍 renderConversationDetails data:', data); 648 564 649 565 var sessionData = data.session || data; 650 console.log('📋 Session data:', sessionData); 651 console.log('📋 Data keys:', Object.keys(data)); 652 566 653 567 var notes = []; 654 568 var tags = []; 655 569 656 570 // Check all possible locations for tags and notes 657 console.log('🔍 Checking for notes and tags in data structure...');658 console.log('🔍 data.adminNotes:', data.adminNotes);659 console.log('🔍 data.tags:', data.tags);660 console.log('🔍 data.metadata:', data.metadata);661 console.log('🔍 sessionData.metadata:', sessionData?.metadata);662 663 571 if (data.adminNotes && Array.isArray(data.adminNotes)) { 664 572 notes = data.adminNotes; 665 console.log('✅ Notes from data.adminNotes:', notes); 666 } 667 573 } 574 668 575 if (data.tags && Array.isArray(data.tags)) { 669 576 tags = data.tags; 670 console.log('✅ Tags from data.tags:', tags); 671 } 672 577 } 578 673 579 if (data.metadata?.adminNotes && notes.length === 0) { 674 580 notes = data.metadata.adminNotes; 675 console.log('✅ Notes from data.metadata:', notes); 676 } 677 581 } 582 678 583 if (data.metadata?.tags && tags.length === 0) { 679 584 tags = data.metadata.tags; 680 console.log('✅ Tags from data.metadata:', tags); 681 } 682 585 } 586 683 587 if (sessionData?.metadata?.adminNotes && notes.length === 0) { 684 588 notes = sessionData.metadata.adminNotes; 685 console.log('✅ Notes from sessionData.metadata:', notes); 686 } 687 589 } 590 688 591 if (sessionData?.metadata?.tags && tags.length === 0) { 689 592 tags = sessionData.metadata.tags; 690 console.log('✅ Tags from sessionData.metadata:', tags); 691 } 692 693 console.log('🏷️ Final tags:', tags); 694 console.log('📝 Final notes:', notes); 593 } 695 594 696 595 // Render conversation info … … 713 612 wpUserId = String(sessionData.userInfo.wpUserId); 714 613 } 715 716 // 🔍 DEBUG: Log all extracted values717 console.log('🔍 User Info Extraction:', {718 firstName: firstName,719 lastName: lastName,720 userName: userName,721 userEmail: userEmail,722 userId: userId,723 wpUserId: wpUserId,724 isGuest: isGuest,725 rawSessionData: sessionData,726 rawData: data727 });728 614 729 615 // ✅ FIX: Build display name with priority: firstName+lastName → userName → email … … 790 676 // ✅ NEW: Fetch WordPress user info if we have wpUserId but no displayName 791 677 if (shouldFetchUserInfo && wpUserId) { 792 console.log('🔄 Fetching WordPress user info for wpUserId:', wpUserId);793 678 $.ajax({ 794 679 url: convertyBotConversations.ajax_url, … … 799 684 }, 800 685 success: function(response) { 801 console.log('✅ WordPress user info response:', response);802 686 if (response.success && response.data) { 803 687 var fetchedName = response.data.displayName || response.data.userName || response.data.userLogin; 804 var fetchedEmail = response.data.userEmail;805 688 if (fetchedName) { 806 689 var $nameElement = $('#' + userNameElementId); 807 690 $nameElement.html('<strong>👤 Name:</strong> ' + fetchedName); 808 console.log('✅ Updated user name display to:', fetchedName);809 691 } 810 692 } 811 693 }, 812 694 error: function(xhr, status, error) { 813 console.log('⚠️ Failed to fetch WordPress user info:', error);814 695 // Keep showing "WordPress User #X" as fallback 815 696 var $nameElement = $('#' + userNameElementId); … … 821 702 var messagesContainer = $('#messagesContainer'); 822 703 messagesContainer.empty(); 823 824 console.log('🔍 Messages count:', data.messages ? data.messages.length : 0); 825 704 826 705 if (data.messages && data.messages.length > 0) { 827 706 data.messages.sort(function(a, b) { … … 869 748 messagesContainer.append(html); 870 749 } catch (error) { 871 console.error(' ❌Message render error:', error);750 console.error('Message render error:', error); 872 751 messagesContainer.append('<div class="message-error">Error rendering message</div>'); 873 752 } … … 882 761 renderAdminNotes: function(notes, tags) { 883 762 var self = this; 884 console.log('🔍 renderAdminNotes - Notes:', notes, 'Tags:', tags); 885 763 886 764 var container = $('#notesContainer'); 887 765 container.empty(); 888 766 889 767 if (tags && tags.length > 0) { 890 console.log('🏷️ Rendering tags:', tags);891 768 var tagsHtml = '<div class="tags-section" style="background: #f0f8ff; padding: 15px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #0066cc;">'; 892 769 tagsHtml += '<h4 style="margin: 0 0 10px 0; color: #0066cc;">🏷️ Tags (' + tags.length + ')</h4>'; … … 900 777 901 778 if (notes && notes.length > 0) { 902 console.log('📝 Rendering notes:', notes);903 779 var notesHtml = '<div class="notes-section" style="background: #f9f9f9; padding: 15px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #0073aa;">'; 904 780 notesHtml += '<h4 style="margin: 0 0 10px 0; color: #0073aa;">📝 Admin Notes (' + notes.length + ')</h4>'; 905 781 notes.forEach(function(note, index) { 906 console.log('📝 Rendering note ' + (index + 1) + ':', note);907 782 notesHtml += '<div class="note-item" style="background: white; padding: 10px; margin: 8px 0; border-radius: 3px; border: 1px solid #ddd;">'; 908 783 notesHtml += '<div class="note-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; font-size: 12px; color: #666;">'; … … 935 810 container.append(notesHtml); 936 811 } else { 937 console.log('📝 No notes to display');938 812 container.append('<div class="no-notes" style="text-align: center; padding: 20px; color: #666; background: #f9f9f9; border-radius: 5px;"><p>📝 No admin notes yet.</p></div>'); 939 813 } … … 978 852 // Export all conversations from backend, not just visible ones 979 853 exportAll = true; 980 console.log('📤 Export All mode - will fetch all conversations from server');981 854 } 982 855 … … 1008 881 }; 1009 882 1010 console.log('📤 Export request data:', data);1011 1012 883 $.post(convertyBotConversations.ajaxurl, data) 1013 884 .done(function(response) { 1014 console.log('📥 Export response:', response);1015 console.log('📥 Export response.data:', response.data);1016 console.log('📥 Export response.data.conversations:', response.data ? response.data.conversations : 'N/A');1017 1018 885 if (response.success && response.data) { 1019 886 // Create download from the received data … … 1021 888 var filename, content, mimeType; 1022 889 1023 console.log('📦 Export data object:', exportData);1024 console.log('📦 Has conversations?', !!exportData.conversations);1025 console.log('📦 Conversations count:', exportData.conversations ? exportData.conversations.length : 0);1026 1027 890 if (exportData.downloadUrl) { 1028 891 // If backend provides a download URL, use it 1029 console.log('🔗 Using download URL:', exportData.downloadUrl);1030 892 window.open(exportData.downloadUrl, '_blank'); 1031 893 } else if (exportData.conversations && exportData.conversations.length > 0) { … … 1033 895 var formatType = exportData.format || format; 1034 896 var timestamp = new Date().toISOString().slice(0, 10); 1035 1036 console.log('📝 Creating file with format:', formatType);1037 897 1038 898 if (formatType === 'json') { … … 1055 915 } 1056 916 1057 console.log('📁 File details:', { filename: filename, mimeType: mimeType, contentLength: content.length });1058 1059 917 // Trigger download 1060 918 try { … … 1066 924 link.style.display = 'none'; 1067 925 document.body.appendChild(link); 1068 console.log('📥 Triggering download...');1069 926 link.click(); 1070 927 … … 1073 930 document.body.removeChild(link); 1074 931 URL.revokeObjectURL(url); 1075 console.log('✅ Download triggered successfully');1076 932 }, 100); 1077 933 } catch (downloadError) { 1078 console.error(' ❌Download error:', downloadError);934 console.error('Download error:', downloadError); 1079 935 self.showError('Failed to create download: ' + downloadError.message); 1080 936 return; 1081 937 } 1082 938 } else { 1083 console.warn('⚠️ No conversations in export data');1084 939 self.showError('No conversations data received from server'); 1085 940 return; … … 1089 944 self.hideExportModal(); 1090 945 } else { 1091 console.error('❌ Export failed:', response);1092 946 self.showError((response && response.data) || (convertyBotConversations.strings && convertyBotConversations.strings.export_error) || 'Export failed'); 1093 947 } 1094 948 }) 1095 949 .fail(function(xhr, status, error) { 1096 console.error('❌ Export AJAX failed:', status, error);1097 950 self.showError((convertyBotConversations.strings && convertyBotConversations.strings.export_error) || 'Export failed: ' + error); 1098 951 }) … … 1288 1141 }) 1289 1142 .fail(function() { 1290 console.warn('Failed to load current tags');1143 // Failed to load current tags 1291 1144 }); 1292 1145 }, … … 1326 1179 priority: priority 1327 1180 }; 1328 1329 console.log('📤 Quick note data:', data); 1181 1330 1182 $('#saveQuickNote').prop('disabled', true).text('Saving...'); 1331 1183 … … 1407 1259 1408 1260 $('#saveNote').prop('disabled', true).text('Saving...'); 1409 1410 console.log('📤 Sending note data:', data); 1411 console.log('📤 AJAX URL:', convertyBotConversations.ajaxurl); 1261 1412 1262 $.post(convertyBotConversations.ajaxurl, data) 1413 1263 .done(function(response) { 1414 console.log('💾 Note save response:', response);1415 console.log('💾 Response type:', typeof response);1416 1264 if (response.success) { 1417 1265 self.showSuccess((convertyBotConversations.strings && convertyBotConversations.strings.note_saved) || 'Note saved successfully'); 1418 1266 self.hideAddNoteForm(); 1419 1267 1420 1268 // Force reload conversation data 1421 console.log('🔄 Reloading conversation after note save...');1422 1269 setTimeout(function() { 1423 1270 self.viewConversation(self.config.currentConversationId); … … 1468 1315 $.post(convertyBotConversations.ajaxurl, data) 1469 1316 .done(function(response) { 1470 console.log('🏷️ Tag save response:', response);1471 1317 if (response.success) { 1472 1318 self.showSuccess('Tags saved successfully'); 1473 1319 self.hideAddTagForm(); 1474 1320 1475 1321 // Force reload conversation data 1476 console.log('🔄 Reloading conversation after tag save...');1477 1322 setTimeout(function() { 1478 1323 self.viewConversation(self.config.currentConversationId); … … 1648 1493 // Ensure status is a string 1649 1494 if (typeof status !== 'string') { 1650 console.warn('getStatusLabel received non-string status:', status);1651 1495 status = 'active'; 1652 1496 } … … 1663 1507 1664 1508 var result = labels[status] || status.charAt(0).toUpperCase() + status.slice(1); 1665 console.log('getStatusLabel:', status, '->', result);1666 1509 return result; 1667 1510 }, … … 1698 1541 1699 1542 formatTimeAgo: function(timestamp) { 1700 console.log('formatTimeAgo input:', timestamp, 'type:', typeof timestamp);1701 1543 if (!timestamp) { 1702 console.warn('formatTimeAgo: timestamp is falsy, returning Just now');1703 1544 return 'Just now'; 1704 1545 } … … 1878 1719 updatePagination: function(pagination) { 1879 1720 if (!pagination) { 1880 console.log('⚠️ updatePagination: no pagination data');1881 1721 return; 1882 1722 } 1883 1884 console.log('📄 updatePagination called with:', pagination);1885 1723 1886 1724 this.config.totalConversations = pagination.total; … … 1890 1728 this.config.currentPage = pagination.currentPage; 1891 1729 } 1892 1893 console.log('📄 Updated config:', {1894 currentPage: this.config.currentPage,1895 totalPages: this.config.totalPages,1896 totalConversations: this.config.totalConversations1897 });1898 1730 1899 1731 var paginationHtml = ''; -
convertybot/trunk/assets/js/admin-coupons.js
r3437100 r3445725 55 55 const isActive = $(this).is(':checked'); 56 56 57 console.log('Toggle status - Rule ID:', ruleId, 'Is Active:', isActive);58 59 57 $.ajax({ 60 58 url: ajaxurl, … … 67 65 }, 68 66 success: function(response) { 69 console.log('Toggle response:', response);70 67 if (!response.success) { 71 68 alert('Failed to update rule status: ' + response.data); … … 75 72 }, 76 73 error: function(xhr, status, error) { 77 console.error('Toggle error:', error);78 74 alert('Error toggling status: ' + error); 79 75 // Revert toggle … … 171 167 location.reload(); 172 168 } else { 173 console.error('Failed to save rule:', response.data);174 169 alert('Failed to save rule: ' + response.data); 175 170 } 176 171 }, 177 172 error: function(xhr, status, error) { 178 console.error('AJAX ERROR:', {179 status: status,180 error: error,181 response: xhr.responseText,182 statusCode: xhr.status183 });184 173 alert('AJAX Error: ' + error); 185 174 } … … 228 217 $('#min-messages').val(conditions.min_messages || ''); 229 218 } catch (e) { 230 console.warn('Failed to parse conditions JSON:', e);219 // Failed to parse conditions JSON 231 220 } 232 221 } -
convertybot/trunk/assets/js/admin-subscription.js
r3437100 r3445725 291 291 }, 292 292 success: function(response) { 293 console.log('Upgrade response:', response);294 293 if (response.success && response.data) { 295 294 if (response.data.upgraded) { … … 318 317 }, 319 318 error: function(xhr) { 320 console.error('Upgrade API Error:', xhr);321 319 var error = convertyBotSubscription.strings.unknownError; 322 320 … … 348 346 var billingPeriod = $('#billing-period-toggle').is(':checked') ? 'yearly' : 'monthly'; 349 347 350 console.log('Upgrade request:', {351 tier: tier,352 billingPeriod: billingPeriod,353 apiUrl: convertyBotSubscription.apiUrl + '/v1/shops/upgrade'354 });355 356 348 // Get tier info for display 357 349 var newTierInfo = tierInfo[tier] || { name: tier, monthlyPrice: '', yearlyPrice: '' }; … … 392 384 function() { 393 385 // On cancel - do nothing 394 console.log('Upgrade cancelled by user');395 386 }, 396 387 isDowngrade // isDanger flag for styling -
convertybot/trunk/assets/js/admin-visitor-journeys.js
r3443877 r3445725 44 44 */ 45 45 function init() { 46 console.log('Initializing Visitor Journeys Dashboard');47 console.log('Using WordPress AJAX URL:', typeof ajaxurl !== 'undefined' ? ajaxurl : 'Not defined');48 49 46 // Validate configuration 50 47 if (!validateConfiguration()) { … … 66 63 function validateConfiguration() { 67 64 if (typeof ajaxurl === 'undefined' || !ajaxurl || ajaxurl.trim() === '') { 68 console.error('WordPress AJAX URL is missing');69 65 return false; 70 66 } … … 127 123 e.preventDefault(); 128 124 const page = $(this).data('page'); 129 console.log('[Pagination] Clicked page:', page, 'Current page:', currentFilters.page);130 125 if (page && page !== currentFilters.page) { 131 126 currentFilters.page = page; 132 console.log('[Pagination] Loading page:', currentFilters.page);133 127 loadSessions(); 134 } else {135 console.log('[Pagination] Same page, not reloading');136 128 } 137 129 }); … … 200 192 201 193 const params = buildDateParams(); 202 203 // Debug: log NONCE value204 console.log('[Overview] NONCE value:', NONCE, 'Config:', config);205 194 206 195 activeRequests.overview = $.ajax({ … … 218 207 updateOverviewStats(response.data.overview); 219 208 $('#stats-overview').slideDown(); 220 } else {221 console.warn('Unexpected response format:', response);222 209 } 223 210 }, … … 227 214 // Don't retry if request was aborted 228 215 if (status === 'abort') { 229 console.log('Overview request aborted');230 216 return; 231 217 } 232 218 233 console.error('Error loading overview stats:', error, 'Status:', status);234 console.error('Response:', xhr.responseText);235 236 219 // Retry logic for network errors 237 220 if (retryCount < MAX_RETRIES && (status === 'timeout' || status === 'error')) { 238 console.log(`Retrying overview stats (attempt ${retryCount + 1}/${MAX_RETRIES})...`);239 221 setTimeout(function() { 240 222 loadOverviewStats(retryCount + 1); … … 285 267 }; 286 268 287 console.log('[Sessions] Request data:', requestData, 'NONCE:', NONCE);288 289 269 activeRequests.sessions = $.ajax({ 290 270 url: ajaxurl, … … 296 276 hideLoading(); 297 277 298 console.log('[Sessions] Response:', response);299 300 278 if (response.success && response.data) { 301 279 sessionsData = response.data; 302 console.log('[Sessions] Pagination received:', response.data.pagination);303 280 renderSessionsList(response.data); 304 281 renderPagination(response.data.pagination); … … 310 287 } 311 288 } else { 312 console.warn('Unexpected response format:', response);313 289 showError('Unexpected data format received.'); 314 290 } … … 319 295 // Don't retry if request was aborted 320 296 if (status === 'abort') { 321 console.log('Sessions request aborted');322 297 hideLoading(); 323 298 return; 324 299 } 325 300 326 console.error('Error loading sessions:', error, 'Status:', status);327 console.error('Response:', xhr.responseText);328 301 hideLoading(); 329 302 330 303 // Retry logic for network errors 331 304 if (retryCount < MAX_RETRIES && (status === 'timeout' || status === 'error')) { 332 console.log(`Retrying sessions (attempt ${retryCount + 1}/${MAX_RETRIES})...`);333 305 setTimeout(function() { 334 306 loadSessions(retryCount + 1); … … 647 619 $('#journey-content').fadeIn(); 648 620 } else { 649 console.warn('Unexpected response format:', response);650 621 showError('Unexpected data format received.'); 651 622 } … … 657 628 // Don't retry if request was aborted 658 629 if (status === 'abort') { 659 console.log('Journey request aborted');660 630 return; 661 631 } 662 632 663 console.error('Error loading journey details:', error, 'Status:', status);664 console.error('Response:', xhr.responseText);665 666 633 // Retry logic for network errors 667 634 if (retryCount < MAX_RETRIES && (status === 'timeout' || status === 'error')) { 668 console.log(`Retrying journey details (attempt ${retryCount + 1}/${MAX_RETRIES})...`);669 635 setTimeout(function() { 670 636 loadJourneyDetails(sessionId, retryCount + 1); … … 758 724 timeline.forEach(function(item) { 759 725 // Filter out empty/whitespace-only insights before checking 760 // Handle undefined, null, or empty arrays 761 const filteredInsights = (item.insights && Array.isArray(item.insights)) 762 ? item.insights.filter(function(insight) { 763 return insight && typeof insight === 'string' && insight.trim().length > 0; 764 }) 765 : []; 726 // Handle undefined, null, empty arrays, and non-string values 727 let filteredInsights = []; 728 if (item.insights && Array.isArray(item.insights)) { 729 filteredInsights = item.insights.filter(function(insight) { 730 // Must be a string and not empty or whitespace-only 731 return typeof insight === 'string' && insight.trim(); 732 }); 733 } 766 734 767 735 const itemData = { … … 781 749 let itemHtml = template; 782 750 783 // Replace simple variables 751 // Replace simple variables (but NOT {{this}} which is used inside #each loops) 784 752 itemHtml = itemHtml.replace(/\{\{(\w+)\}\}/g, function(match, key) { 753 if (key === 'this') return match; // Preserve {{this}} for #each processing 785 754 return itemData[key] !== undefined ? itemData[key] : ''; 786 755 }); … … 829 798 content = content.replace(/\{\{#each insights\}\}([\s\S]*?)\{\{\/each\}\}/g, function(match, itemContent) { 830 799 return data.insights.map(function(insight) { 831 // Only render if insight text isnot empty832 if ( insight&& insight.trim()) {833 return itemContent.replace(/\{\{this\}\}/g, insight);800 // Only render if insight is a string and not empty 801 if (typeof insight === 'string' && insight.trim()) { 802 return itemContent.replace(/\{\{this\}\}/g, escapeHtml(insight)); 834 803 } 835 804 return ''; … … 842 811 }); 843 812 813 // EXTRA: Remove any remaining empty timeline-insights divs 814 html = html.replace(/<div class="timeline-insights">\s*<\/div>/g, ''); 815 844 816 // Clean up any remaining template syntax 845 817 html = html.replace(/\{\{#if \w+\}\}/g, ''); … … 850 822 return html; 851 823 } 824 852 825 853 826 /** … … 998 971 notice.fadeOut(function() { $(this).remove(); }); 999 972 }, 10000); 1000 1001 // Also log to console for debugging1002 console.error('User Error:', message);1003 973 } 1004 974 -
convertybot/trunk/assets/js/admin.js
r3444237 r3445725 456 456 function loadAnalyticsCharts() { 457 457 if (typeof Chart === 'undefined') { 458 console.warn('Chart.js not loaded');459 458 return; 460 459 } … … 621 620 } 622 621 623 // Log the current state for debugging624 console.log('AI Chatbot Toggle State:', {625 chatbot_enabled: chatbotEnabled,626 auto_open: $('#auto_open').is(':checked'),627 engagement_tracking: $('#engagement_tracking').is(':checked'),628 sound_enabled: $('#sound_enabled').is(':checked')629 });630 622 } 631 623 … … 840 832 conditions = typeof data.conditions === 'string' ? JSON.parse(data.conditions) : data.conditions; 841 833 } catch (e) { 842 console.warn('Failed to parse conditions JSON:', data.conditions);843 834 conditions = {}; 844 835 } -
convertybot/trunk/assets/js/analytics-comprehensive.js
r3437100 r3445725 53 53 this.setupTouchInteractions(); 54 54 this.setupAccessibilityFeatures(); 55 56 console.log('Comprehensive Analytics Dashboard initialized');57 55 } 58 56 … … 117 115 // Prevent modal close on content click 118 116 $('.modal-content').on('click', (e) => e.stopPropagation()); 119 120 console.log('Event listeners setup complete');121 117 } 122 118 … … 148 144 this.initializeBehavioralPatternsChart(); 149 145 this.initializeForecastChart(); 150 151 console.log('Charts initialization complete');152 146 } 153 147 … … 928 922 929 923 this.websocket.onopen = () => { 930 console.log('WebSocket connection established');931 924 this.updateRealTimeStatus(true); 932 925 }; … … 938 931 939 932 this.websocket.onclose = () => { 940 console.log('WebSocket connection closed');941 933 this.updateRealTimeStatus(false); 942 934 // Attempt to reconnect after 5 seconds … … 965 957 this.updateRealTimeMetrics(); 966 958 }, this.config.refreshInterval); 967 968 console.log('Polling setup complete');969 959 } 970 960 … … 984 974 break; 985 975 default: 986 console.log('Unknown real-time update type:', data.type);976 break; 987 977 } 988 978 } … … 1465 1455 showMetricDrillDown(metricType) { 1466 1456 // This would typically show a modal with detailed breakdown 1467 console.log('Showing drill-down for:', metricType);1468 1469 1457 if (window.aiTrack) { 1470 1458 window.aiTrack('metric_drill_down', { metric: metricType }); … … 1678 1666 document.removeEventListener('keydown', this.handleKeydown); 1679 1667 window.removeEventListener('resize', this.handleResize); 1680 1681 console.log('Analytics dashboard destroyed');1682 1668 } 1683 1669 } -
convertybot/trunk/assets/js/analytics-enhanced.js
r3437100 r3445725 59 59 init() { 60 60 if (!this.shouldTrack()) { 61 console.log('Analytics: Tracking disabled due to sampling or privacy settings');62 61 return; 63 62 } … … 69 68 this.trackPageLoad(); 70 69 this.startPeriodicFlush(); 71 72 console.log('Analytics: Advanced tracking initialized');73 70 } 74 71 … … 421 418 this.flush(); 422 419 } 423 424 console.log('Analytics: Event tracked', eventType, properties);425 420 } 426 421 … … 548 543 // Skip if no nonce available (indicates WordPress context issues) 549 544 if (!nonce) { 550 console.warn('Analytics: No nonce available, skipping analytics data send');551 545 return; 552 546 } … … 600 594 601 595 const result = await response.json(); 602 if (result && !result.success && result.data) { 603 console.warn('Analytics: Server returned error:', result.data); 604 } 605 } 606 607 console.log('Analytics: Data sent successfully', data.events?.length || 0, 'events'); 596 } 608 597 } catch (error) { 609 // Different handling based on error type610 if (error.name === 'AbortError') {611 console.warn('Analytics: Request timed out');612 } else if (error.message.includes('404')) {613 console.warn('Analytics: Endpoint not available, data will be stored for retry');614 } else {615 console.error('Analytics: Failed to send data', error.message);616 }617 618 598 // Store failed events in localStorage for retry (but limit storage) 619 599 if (!isBeacon && data.events && data.events.length > 0) { … … 636 616 localStorage.setItem('convertybot_failed_events', JSON.stringify(failedEvents)); 637 617 } catch (error) { 638 console.error('Analytics: Failed to store events', error);618 // Storage error - silently fail 639 619 } 640 620 } … … 647 627 const failedEvents = JSON.parse(stored); 648 628 if (failedEvents.length === 0) return; 649 650 console.log('Analytics: Retrying', failedEvents.length, 'failed events'); 651 629 652 630 this.sendAnalyticsData({ events: failedEvents, metrics: {}, heatmap: [], performance: {}, funnel: {} }); 653 631 localStorage.removeItem('convertybot_failed_events'); 654 632 } catch (error) { 655 console.error('Analytics: Failed to retry events', error);633 // Retry error - silently fail 656 634 } 657 635 } … … 726 704 window.convertyBotAnalytics.trackConversion(type, value, properties); 727 705 }; 728 729 console.log('Analytics: Advanced system initialized');730 706 } 731 707 -
convertybot/trunk/assets/js/analytics-redesigned.js
r3437100 r3445725 82 82 this.setupKeyboardNavigation(); 83 83 84 console.log('✅ Analytics Dashboard initialized successfully');85 84 this.announceToScreenReader('Analytics dashboard loaded successfully'); 86 85 } catch (error) { 87 console.error(' ❌Failed to initialize Analytics Dashboard:', error);86 console.error('Failed to initialize Analytics Dashboard:', error); 88 87 this.showNotification('Failed to initialize dashboard', 'error'); 89 88 } … … 229 228 this.measurePerformance('initial_load', performance.now()); 230 229 }); 231 232 console.log('📋 Event listeners setup complete');233 230 } 234 231 … … 267 264 // Check for accessibility preferences 268 265 this.checkAccessibilityPreferences(); 269 270 console.log('♿ Accessibility features setup complete');271 266 } 272 267 … … 363 358 async initializeCharts() { 364 359 if (typeof Chart === 'undefined') { 365 console.warn('Chart.js not loaded, charts will not be available');366 360 return; 367 361 } … … 387 381 const loadTime = performance.now() - startTime; 388 382 this.measurePerformance('charts_init', loadTime); 389 390 console.log(`📊 Charts initialized in ${loadTime.toFixed(2)}ms`);391 383 } catch (error) { 392 384 console.error('Failed to initialize charts:', error); … … 519 511 520 512 this.charts.set('revenue', chart); 521 console.log('📈 Revenue chart initialized');522 513 } finally { 523 514 this.showChartLoading('revenue-chart-loading', false); … … 584 575 585 576 this.charts.set('funnel', chart); 586 console.log('🔽 Funnel chart initialized');587 577 } catch (error) { 588 578 console.error('Failed to initialize funnel chart:', error); … … 623 613 624 614 this.charts.set('aiGauge', chart); 625 console.log('🎯 AI Gauge chart initialized');626 615 } catch (error) { 627 616 console.error('Failed to initialize AI gauge chart:', error); … … 674 663 675 664 this.charts.set('device', chart); 676 console.log('📱 Device chart initialized');677 665 } catch (error) { 678 666 console.error('Failed to initialize device chart:', error); … … 730 718 731 719 this.charts.set('heatmap', chart); 732 console.log('🔥 Heatmap initialized');733 720 } catch (error) { 734 721 console.error('Failed to initialize heatmap:', error); … … 809 796 const loadTime = performance.now() - startTime; 810 797 this.measurePerformance('data_load', loadTime); 811 812 console.log(`💾 Dashboard data loaded in ${loadTime.toFixed(2)}ms`);813 798 } else { 814 799 throw new Error(response.message || 'Failed to load dashboard data'); … … 836 821 // Update comparison indicators 837 822 this.updateComparisonIndicators(); 838 839 console.log('📊 Dashboard metrics updated');840 823 } 841 824 … … 918 901 919 902 this.websocket.onopen = () => { 920 console.log('🔌 WebSocket connected');921 903 this.updateConnectionStatus(true); 922 904 this.announceToScreenReader('Real-time updates connected'); … … 933 915 934 916 this.websocket.onclose = (event) => { 935 console.log('🔌 WebSocket disconnected:', event.code);936 917 this.updateConnectionStatus(false); 937 918 … … 974 955 975 956 this.updateConnectionStatus(true); 976 console.log('⏰ Polling setup complete');977 957 } 978 958 … … 995 975 break; 996 976 default: 997 console.log('Unknown real-time update type:', data.type);977 break; 998 978 } 999 979 } … … 1474 1454 pullDistance = 0; 1475 1455 }, { passive: true }); 1476 1477 console.log('👆 Touch interactions setup complete');1478 1456 } 1479 1457 … … 1518 1496 } 1519 1497 }); 1520 1521 console.log('⌨️ Keyboard navigation setup complete');1522 1498 } 1523 1499 … … 1765 1741 }); 1766 1742 } 1767 1768 console.log('📊 Event tracked:', eventName, properties);1769 1743 } 1770 1744 … … 1939 1913 this.announcer.parentNode.removeChild(this.announcer); 1940 1914 } 1941 1942 console.log('🧹 Analytics dashboard cleanup complete');1943 1915 } 1944 1916 … … 1998 1970 } 1999 1971 }; 2000 2001 console.log('🚀 ConvertyBot Analytics Dashboard JavaScript loaded'); -
convertybot/trunk/assets/js/analytics-websocket.js
r3437100 r3445725 54 54 init() { 55 55 if (!this.config.url) { 56 console.warn('WebSocket URL not configured, falling back to polling');57 56 this.enableFallbackMode(); 58 57 return; … … 69 68 connect() { 70 69 if (this.socket && this.socket.readyState === WebSocket.OPEN) { 71 console.log('WebSocket already connected');72 70 return; 73 71 } … … 84 82 url.searchParams.set('client_id', this.getClientId()); 85 83 86 console.log('Connecting to WebSocket:', url.toString());87 88 84 this.socket = new WebSocket( 89 85 url.toString(), … … 104 100 setupSocketEventHandlers() { 105 101 this.socket.onopen = (event) => { 106 console.log('WebSocket connected');107 102 this.connectionState = 'connected'; 108 103 this.metrics.lastConnected = new Date(); … … 123 118 124 119 this.socket.onclose = (event) => { 125 console.log('WebSocket connection closed:', event.code, event.reason);126 120 this.connectionState = 'disconnected'; 127 121 this.metrics.lastDisconnected = new Date(); … … 170 164 break; 171 165 default: 172 console.log('Unknown message type:', message.type);166 break; 173 167 } 174 168 … … 264 258 switch (action) { 265 259 case 'server_restart': 266 console.log('Server restart detected, will reconnect');267 260 this.scheduleReconnection(5000); // Wait 5 seconds 268 261 break; 269 262 case 'maintenance_mode': 270 console.log('Server entering maintenance mode');271 263 this.enableFallbackMode(); 272 264 break; … … 420 412 defaultChannels.forEach(channel => { 421 413 this.subscribe(channel, (data) => { 422 console.log(`Received data for ${channel}:`, data);414 // Handle data silently 423 415 }); 424 416 }); … … 480 472 scheduleReconnection(delay = null) { 481 473 if (!this.shouldReconnect()) { 482 console.log('Max reconnection attempts reached or conditions not met');483 474 this.enableFallbackMode(); 484 475 return; … … 489 480 this.config.maxReconnectDelay 490 481 ); 491 492 console.log(`Scheduling reconnection in ${reconnectDelay}ms (attempt ${this.reconnectCount + 1})`);493 482 494 483 setTimeout(() => { … … 508 497 // Try fallback URL if available 509 498 if (this.config.fallbackUrl && this.config.url !== this.config.fallbackUrl) { 510 console.log('Trying fallback WebSocket URL');511 499 this.config.url = this.config.fallbackUrl; 512 500 this.scheduleReconnection(1000); … … 520 508 */ 521 509 enableFallbackMode() { 522 console.log('Enabling fallback polling mode');523 510 this.connectionState = 'fallback'; 524 511 this.notifyStateChange('fallback'); … … 582 569 // Handle online/offline events 583 570 window.addEventListener('online', () => { 584 console.log('Network connection restored');585 571 if (this.connectionState === 'disconnected') { 586 572 this.connect(); … … 589 575 590 576 window.addEventListener('offline', () => { 591 console.log('Network connection lost');592 577 this.pauseConnection(); 593 578 }); … … 680 665 implementRateLimit(retryAfter) { 681 666 const delay = (retryAfter || 60) * 1000; // Convert to milliseconds 682 console.log(`Rate limited, waiting ${delay}ms before retry`); 683 667 684 668 this.pauseConnection(); 685 669 setTimeout(() => { … … 693 677 updateConfig(newConfig) { 694 678 Object.assign(this.config, newConfig); 695 console.log('Configuration updated:', newConfig);696 679 } 697 680 … … 771 754 if (this.eventHandlers.hasOwnProperty(`on${event.charAt(0).toUpperCase() + event.slice(1)}`)) { 772 755 this.eventHandlers[`on${event.charAt(0).toUpperCase() + event.slice(1)}`] = handler; 773 } else {774 console.warn(`Unknown event type: ${event}`);775 756 } 776 757 } … … 845 826 // Clear message queue 846 827 this.messageQueue = []; 847 848 console.log('WebSocket manager destroyed');849 828 } 850 829 } -
convertybot/trunk/assets/js/chatbot-sounds.js
r3437100 r3445725 54 54 this.sounds[soundName](); 55 55 } catch (error) { 56 console.warn('Failed to play sound:', soundName, error);56 // Failed to play sound 57 57 } 58 58 } -
convertybot/trunk/assets/js/consent-banner.js
r3437100 r3445725 15 15 16 16 init() { 17 console.log('🍪 [ConsentBanner] Initializing...');18 19 17 this.banner = $('#convertybot-consent-banner'); 20 18 this.settingsBtn = $('#ai-consent-settings-btn'); 21 19 22 20 if (this.banner.length === 0) { 23 console.log('🍪 [ConsentBanner] Banner not found, consent already given');24 21 return; 25 22 } 26 23 27 24 this.bindEvents(); 28 console.log('✅ [ConsentBanner] Initialized successfully');29 25 } 30 26 … … 32 28 // Accept all button 33 29 $('#ai-consent-accept').on('click', () => { 34 console.log('✅ [ConsentBanner] User accepted all cookies');35 30 this.saveConsent({ 36 31 essential: true, … … 45 40 // Reject non-essential button 46 41 $('#ai-consent-reject').on('click', () => { 47 console.log('❌ [ConsentBanner] User rejected non-essential cookies');48 42 this.saveConsent({ 49 43 essential: true, … … 58 52 // Save custom preferences button 59 53 $('#ai-consent-save').on('click', () => { 60 console.log('💾 [ConsentBanner] User saving custom preferences');61 54 const preferences = { 62 55 essential: true, // Always true … … 67 60 advertising: false // Not shown in UI yet 68 61 }; 69 console.log('📋 [ConsentBanner] Custom preferences:', preferences);70 62 this.saveConsent(preferences); 71 63 }); … … 73 65 // Settings button (to re-open banner) 74 66 this.settingsBtn.on('click', () => { 75 console.log('⚙️ [ConsentBanner] Opening consent settings');76 67 this.showBanner(); 77 68 }); … … 80 71 $('.ai-consent-overlay').on('click', (e) => { 81 72 if ($(e.target).hasClass('ai-consent-overlay')) { 82 console.log('ℹ️ [ConsentBanner] User clicked overlay, showing reject prompt');83 73 // Don't close without action - prompt user to make a choice 84 74 this.showRejectPrompt(); … … 88 78 89 79 saveConsent(preferences) { 90 console.log('📤 [ConsentBanner] Sending consent to server...', preferences);91 92 80 $.ajax({ 93 81 url: convertyBotConsent.ajaxUrl, … … 100 88 success: (response) => { 101 89 if (response.success) { 102 console.log('✅ [ConsentBanner] Consent saved successfully', response.data);103 90 this.hideBanner(); 104 91 this.notifyConsentChange(preferences); … … 109 96 // Reload page to apply consent changes 110 97 setTimeout(() => { 111 console.log('🔄 [ConsentBanner] Reloading page to apply consent...');112 98 window.location.reload(); 113 99 }, 1000); 114 100 } else { 115 console.error('❌ [ConsentBanner] Failed to save consent', response);116 101 this.showToast('Failed to save preferences. Please try again.', 'error'); 117 102 } 118 103 }, 119 104 error: (xhr, status, error) => { 120 console.error('❌ [ConsentBanner] AJAX error:', error);121 105 this.showToast('Network error. Please try again.', 'error'); 122 106 } … … 125 109 126 110 showBanner() { 127 console.log('👁️ [ConsentBanner] Showing banner');128 111 this.banner.removeClass('hidden').css('display', 'flex'); 129 112 $('body').css('overflow', 'hidden'); // Prevent scrolling … … 131 114 132 115 hideBanner() { 133 console.log('🙈 [ConsentBanner] Hiding banner');134 116 this.banner.addClass('hidden'); 135 117 $('body').css('overflow', ''); // Restore scrolling … … 157 139 }); 158 140 window.dispatchEvent(event); 159 160 console.log('📢 [ConsentBanner] Consent change event dispatched', preferences);161 141 162 142 // Update global tracking SDK if available … … 168 148 }; 169 149 window.convertyBotTracker.updateConsent(consentData); 170 console.log('✅ [ConsentBanner] Updated tracking SDK with new consent');171 150 } 172 151 } -
convertybot/trunk/assets/js/coupon-system-enhanced.js
r3437100 r3445725 60 60 // Clean up expired coupons on startup 61 61 this.cleanupExpiredCoupons(); 62 63 console.log('Enhanced Coupon System initialized');64 62 } 65 63 … … 73 71 } 74 72 } catch (error) { 75 console.error('Failed to load coupon stats:', error);73 // Failed to load coupon stats 76 74 } 77 75 } … … 83 81 localStorage.setItem('convertybot_coupon_stats', JSON.stringify(this.userStats)); 84 82 } catch (error) { 85 console.error('Failed to save coupon stats:', error);83 // Failed to save coupon stats 86 84 } 87 85 } … … 102 100 } 103 101 } catch (error) { 104 console.error('Failed to load active coupons:', error);102 // Failed to load active coupons 105 103 } 106 104 } … … 113 111 localStorage.setItem('convertybot_active_coupons', JSON.stringify(coupons)); 114 112 } catch (error) { 115 console.error('Failed to save active coupons:', error);113 // Failed to save active coupons 116 114 } 117 115 } … … 756 754 processCouponApplication(coupon) { 757 755 // This would integrate with your cart/checkout system 758 console.log('Applying coupon:', coupon);759 760 756 // Show success message 761 757 if (window.convertyBotChatbot?.showToast) { … … 1044 1040 1045 1041 // This would update a gamification widget in the UI 1046 console.log('Gamification Status:', {1047 level: currentLevel,1048 progress: progress,1049 stats: this.userStats1050 });1051 1042 } 1052 1043 -
convertybot/trunk/assets/js/engagement-tracking-sdk.js
r3437100 r3445725 14 14 (function(window, document) { 15 15 'use strict'; 16 17 // ✅ FIX Bug 6: Disable console logs in production mode18 // Check if convertyBotTrackingConfig.debug is true before logging19 const isDebugMode = typeof window.convertyBotTrackingConfig !== 'undefined' && window.convertyBotTrackingConfig.debug === true;20 if (!isDebugMode) {21 const noop = function() {};22 // Store originals for potential restoration23 window._engagementSDKOriginalConsole = { log: console.log, info: console.info, debug: console.debug };24 console.log = noop;25 console.info = noop;26 console.debug = noop;27 }28 16 29 17 // SDK Configuration … … 53 41 class EngagementTrackingSDK { 54 42 constructor(config = {}) { 55 console.log('[EngagementSDK DEBUG] Received config:', config);56 console.log('[EngagementSDK DEBUG] config.userId:', config.userId);57 console.log('[EngagementSDK DEBUG] config.siteId:', config.siteId);58 59 43 this.config = { ...DEFAULT_CONFIG, ...config }; 60 44 this.sessionId = null; … … 62 46 this.userId = this.config.userId || window.convertyBotGuestId || null; 63 47 this.siteId = this.config.siteId || null; // Initialize from config 64 65 console.log('[EngagementSDK DEBUG] After init - this.userId:', this.userId);66 console.log('[EngagementSDK DEBUG] After init - window.convertyBotGuestId:', window.convertyBotGuestId);67 console.log('[EngagementSDK DEBUG] After init - this.siteId:', this.siteId);68 48 69 49 this.eventBuffer = []; … … 93 73 async init() { 94 74 try { 95 console.log('🚀 [EngagementSDK] Starting initialization...', {96 apiEndpoint: this.config.apiEndpoint,97 siteId: this.config.siteId,98 batchSize: this.config.batchSize,99 flushInterval: this.config.flushInterval100 });101 102 75 // Check privacy compliance 103 76 await this.checkPrivacyCompliance(); 104 77 105 78 if (!this.isTrackingEnabled) { 106 console.warn('⚠️ [EngagementSDK] Tracking disabled due to privacy settings');107 this.log('Tracking disabled due to privacy settings');108 79 return; 109 80 } 110 111 console.log('✅ [EngagementSDK] Privacy check passed, tracking enabled');112 81 113 82 // Initialize session … … 122 91 this.trackPageView(); 123 92 124 console.log('🎉 [EngagementSDK] Initialization complete!', {125 sessionId: this.sessionId,126 userId: this.userId,127 siteId: this.siteId,128 trackingEnabled: this.isTrackingEnabled129 });130 131 this.log('Engagement tracking initialized', {132 sessionId: this.sessionId,133 userId: this.userId134 });135 136 93 } catch (error) { 137 console.error('❌ [EngagementSDK] Failed to initialize', error); 138 this.log('Failed to initialize tracking SDK', error); 94 console.error('[EngagementSDK] Failed to initialize', error); 139 95 } 140 96 } … … 152 108 // Skip consent check if pointing to backend Node.js API 153 109 if (this.config.apiEndpoint && !this.config.apiEndpoint.includes('/wp-json')) { 154 console.log('✅ [EngagementSDK] Using backend API, skipping WordPress consent check');155 110 return; 156 111 } … … 180 135 async initializeSession() { 181 136 try { 182 console.log('🔐 [EngagementSDK] Initializing session...');183 184 137 // Get existing session or create new one 185 138 const existingSession = this.getSessionFromStorage(); 186 139 187 140 if (existingSession) { 188 console.log('✅ [EngagementSDK] Found existing session', {189 sessionId: existingSession.sessionId,190 userId: existingSession.userId,191 lastActivity: new Date(existingSession.lastActivity).toLocaleString()192 });193 194 141 // Update session with current userId and siteId from config if they're missing 195 142 if (!existingSession.userId && this.userId) { 196 console.log('🔄 [EngagementSDK] Updating session with userId from config:', this.userId);197 143 existingSession.userId = this.userId; 198 144 } 199 145 if (!existingSession.siteId && this.siteId) { 200 console.log('🔄 [EngagementSDK] Updating session with siteId from config:', this.siteId);201 146 existingSession.siteId = this.siteId; 202 147 } … … 204 149 var sessionData = existingSession; 205 150 } else { 206 console.log('🆕 [EngagementSDK] No existing session, creating new one...');207 151 var sessionData = await this.createNewSession(); 208 152 } … … 215 159 this.saveSessionToStorage(sessionData); 216 160 217 console.log('✅ [EngagementSDK] Session initialized successfully', {218 sessionId: this.sessionId,219 userId: this.userId,220 siteId: this.siteId221 });222 223 161 // Start real-time tracking 224 162 await this.startRealTimeTracking(); 225 163 226 164 } catch (error) { 227 console.error('❌ [EngagementSDK] Failed to initialize session', error); 228 this.log('Failed to initialize session', error); 165 console.error('[EngagementSDK] Failed to initialize session', error); 229 166 throw error; 230 167 } … … 233 170 async createNewSession() { 234 171 const deviceInfo = this.getDeviceInfo(); 235 236 console.log('📝 [EngagementSDK] Creating new session on server', {237 endpoint: this.config.apiEndpoint + '/tracking/session',238 deviceInfo: deviceInfo,239 url: window.location.href240 });241 172 242 173 const response = await this.makeRequest('/tracking/session', { … … 257 188 }); 258 189 259 console.log('✅ [EngagementSDK] Session created successfully', response.data);260 261 190 return response.data; 262 191 } … … 304 233 } 305 234 } catch (error) { 306 this.log('Failed to get session from storage', error);235 // Silent fail for storage retrieval 307 236 } 308 237 return null; … … 317 246 localStorage.setItem('convertybot_session', JSON.stringify(dataToStore)); 318 247 } catch (error) { 319 this.log('Failed to save session to storage', error);248 // Silent fail for storage save 320 249 } 321 250 } … … 333 262 334 263 this.abTestAssignments = response.data || {}; 335 264 336 265 // Store assignments for consistent experience 337 266 localStorage.setItem('convertybot_ab_tests', JSON.stringify(this.abTestAssignments)); 338 267 339 268 } catch (error) { 340 this.log('Failed to get A/B test assignments', error);341 269 // Try to load from storage 342 270 try { … … 678 606 trackEvent(eventData) { 679 607 if (!this.isTrackingEnabled) { 680 console.warn('⚠️ [EngagementSDK] Tracking disabled - event not tracked', eventData);681 608 return; 682 609 } … … 693 620 this.eventBuffer.push(event); 694 621 695 console.log('🎯 [EngagementSDK] Event tracked', {696 type: event.type,697 sessionId: this.sessionId,698 bufferSize: this.eventBuffer.length,699 willFlush: this.eventBuffer.length >= this.config.batchSize,700 eventData: event701 });702 703 622 // Flush if buffer is full 704 623 if (this.eventBuffer.length >= this.config.batchSize) { 705 console.log('📦 [EngagementSDK] Buffer full, triggering flush', {706 bufferSize: this.eventBuffer.length,707 batchSize: this.config.batchSize708 });709 624 this.flush(); 710 625 } 711 712 this.log('Event tracked', event);713 626 } 714 627 … … 741 654 userAgent: navigator.userAgent 742 655 } 743 }).catch( error=> {744 this.log('Failed to track product interaction', error);656 }).catch(() => { 657 // Silent fail for product interaction tracking 745 658 }); 746 659 } … … 756 669 ...conversionData 757 670 } 758 }).catch( error=> {759 this.log('Failed to track conversion', error);671 }).catch(() => { 672 // Silent fail for conversion tracking 760 673 }); 761 674 } … … 785 698 }); 786 699 } catch (error) { 787 this.log('Failed to start real-time tracking', error);700 // Silent fail for real-time tracking start 788 701 } 789 702 } … … 793 706 794 707 const events = this.eventBuffer.splice(0); 795 const eventTypes = events.map(e => e.type).join(', ');796 797 console.log('📤 [EngagementSDK] Sending batch events to server', {798 count: events.length,799 types: eventTypes,800 sessionId: this.sessionId,801 endpoint: this.config.apiEndpoint + '/tracking/events/batch'802 });803 708 804 709 try { 805 const response =await this.makeRequest('/tracking/events/batch', {710 await this.makeRequest('/tracking/events/batch', { 806 711 method: 'POST', 807 712 data: { … … 814 719 }); 815 720 816 console.log('✅ [EngagementSDK] Batch events sent successfully', {817 count: events.length,818 response: response819 });820 821 this.log('Flushed events successfully', { count: events.length });822 823 721 } catch (error) { 824 console.error('❌ [EngagementSDK] Failed to send batch events', {825 error: error.message,826 count: events.length,827 eventTypes: eventTypes828 });829 830 this.log('Failed to flush events', error);831 722 // Re-add events to buffer for retry 832 723 this.eventBuffer.unshift(...events); … … 1101 992 1102 993 generateId() { 1103 return 'xxxx-xxxx-xxxx'.replace(/[x]/g, () => 994 return 'xxxx-xxxx-xxxx'.replace(/[x]/g, () => 1104 995 (Math.random() * 16 | 0).toString(16) 1105 996 ); 1106 }1107 1108 log(message, data = null) {1109 // Always log tracking events for visibility (use debug flag for internal details only)1110 const isImportantEvent = message.includes('initialized') ||1111 message.includes('Session') ||1112 message.includes('tracked') ||1113 message.includes('sent') ||1114 message.includes('failed') ||1115 message.includes('error');1116 1117 if (isImportantEvent || this.config.debug) {1118 const emoji = this.getLogEmoji(message);1119 const timestamp = new Date().toLocaleTimeString();1120 1121 if (data) {1122 console.log(`${emoji} [EngagementSDK ${timestamp}] ${message}`, data);1123 } else {1124 console.log(`${emoji} [EngagementSDK ${timestamp}] ${message}`);1125 }1126 }1127 }1128 1129 getLogEmoji(message) {1130 if (message.includes('initialized') || message.includes('Session created')) return '🚀';1131 if (message.includes('Page view')) return '📄';1132 if (message.includes('Event tracked')) return '🎯';1133 if (message.includes('Click')) return '👆';1134 if (message.includes('Scroll')) return '📜';1135 if (message.includes('Product')) return '🛍️';1136 if (message.includes('Batch')) return '📦';1137 if (message.includes('sent') || message.includes('Sending')) return '📤';1138 if (message.includes('success')) return '✅';1139 if (message.includes('failed') || message.includes('error')) return '❌';1140 if (message.includes('warning')) return '⚠️';1141 return '📊';1142 997 } 1143 998 -
convertybot/trunk/assets/js/frontend-enhanced.js
r3437100 r3445725 38 38 // Use the new EngagementTrackingSDK if available, otherwise fall back to old tracker 39 39 if (window.convertyBotTracker) { 40 console.log('🔗 [Frontend] Using EngagementTrackingSDK', {41 sessionId: window.convertyBotTracker.sessionId,42 isTrackingEnabled: window.convertyBotTracker.isTrackingEnabled43 });44 40 this.engagementTracker = window.convertyBotTracker; 45 41 46 42 // Add compatibility wrapper methods to match old API 47 43 if (!this.engagementTracker._compatibilityWrapped) { 48 console.log('🔧 [Frontend] Adding compatibility wrapper to SDK');49 44 const originalTrackEvent = this.engagementTracker.trackEvent.bind(this.engagementTracker); 50 45 … … 53 48 if (typeof typeOrData === 'string') { 54 49 // Old API: trackEvent('event_type', { data }) 55 console.log('🔄 [Frontend] Tracking event (old API)', {56 type: typeOrData,57 data: data58 });59 50 originalTrackEvent({ 60 51 type: typeOrData, … … 63 54 } else { 64 55 // New API: trackEvent({ type: 'event_type', metadata: {} }) 65 console.log('🔄 [Frontend] Tracking event (new API)', typeOrData);66 56 originalTrackEvent(typeOrData); 67 57 } … … 70 60 // Add startSession compatibility method 71 61 this.engagementTracker.startSession = function(sessionId) { 72 console.log('📊 [Frontend] Session started with ID:', sessionId); 73 // SDK auto-manages sessions, just log for compatibility 62 // SDK auto-manages sessions 74 63 }; 75 64 76 65 // Add sendFinalStats compatibility method 77 66 this.engagementTracker.sendFinalStats = function() { 78 console.log('📊 [Frontend] Sending final stats...');79 67 // Force flush any pending events 80 68 if (this.flush) { 81 console.log('📤 [Frontend] Flushing pending events');82 69 this.flush(); 83 70 } … … 85 72 86 73 this.engagementTracker._compatibilityWrapped = true; 87 console.log('✅ [Frontend] Compatibility wrapper added successfully');88 74 } 89 75 } else { 90 console.warn('⚠️ [Frontend] EngagementTrackingSDK not found, using AdvancedEngagementTracker fallback');91 76 this.engagementTracker = new AdvancedEngagementTracker(); 92 77 } … … 153 138 // Setup performance monitoring 154 139 this.startPerformanceMonitoring(); 155 156 console.log('ConvertyBot Chatbot initialized successfully'); 157 140 158 141 } catch (error) { 159 142 console.error('Failed to initialize chatbot:', error); … … 261 244 navigator.serviceWorker.register(convertyBotFrontend.serviceWorkerUrl) 262 245 .then((registration) => { 263 console.log('Service Worker registered:', registration);264 246 }) 265 247 .catch((error) => { 266 console.log('Service Worker registration failed:', error);267 248 }); 268 249 } … … 271 252 async initSocket() { 272 253 if (!convertyBotFrontend.socketUrl) { 273 console.warn('ConvertyBot: Socket URL not configured');274 254 return; 275 255 } … … 301 281 const connectionTime = performance.now() - connectionStart; 302 282 this.performanceMetrics.connectionTime = connectionTime; 303 304 console.log(`ConvertyBot: Connected in ${connectionTime.toFixed(2)}ms`); 283 305 284 this.isConnected = true; 306 285 this.isReconnecting = false; … … 314 293 315 294 this.socket.on('disconnect', (reason) => { 316 console.log('ConvertyBot: Disconnected -', reason);317 295 this.isConnected = false; 318 296 this.updateConnectionStatus('offline'); … … 482 460 // Save session before navigation 483 461 this.saveSession(); 484 console.log('💾 Session saved before product navigation');485 462 }); 486 463 … … 585 562 if (response.success) { 586 563 this.sessionId = response.data.session_id; 587 console.log('ConvertyBot: Session started', this.sessionId); 588 564 589 565 // Join socket room if connected 590 566 if (this.socket && this.isConnected) { … … 730 706 isFeedbackHandled(sessionId) { 731 707 if (!sessionId) return false; 732 708 733 709 try { 734 710 const handledFeedback = JSON.parse(localStorage.getItem('convertybot_handled_feedback') || '{}'); 735 711 return handledFeedback[sessionId] || false; 736 712 } catch (error) { 737 console.warn('Failed to check feedback status:', error);738 713 return false; 739 714 } … … 768 743 localStorage.setItem('convertybot_handled_feedback', JSON.stringify(handledFeedback)); 769 744 } 770 771 console.log(`Feedback marked as handled for session ${sessionId} (${action})`);772 745 } catch (error) { 773 console.warn('Failed to mark feedback as handled:', error);774 746 } 775 747 } … … 919 891 920 892 if (isWelcomeMessage) { 921 console.log('🚫 Welcome message detected - removing products');922 893 metadata.products = []; 923 894 } … … 1096 1067 product_id: product.id || product.product_id || 'unknown', 1097 1068 id: product.id || product.product_id || 'unknown', 1098 1069 1099 1070 // Basic info 1100 1071 name: product.name || product.title || 'Product Name', 1101 1072 image_url: product.image_url || product.image || product.featured_image || '/wp-content/uploads/woocommerce-placeholder.png', 1102 1073 1103 1074 // Pricing 1104 1075 price: price, … … 1108 1079 regular_price: price, 1109 1080 on_sale: salePrice && parseFloat(salePrice) < parseFloat(price), 1110 1111 // Rating & Reviews 1081 1082 // Rating & Reviews 1112 1083 rating: rating, 1113 1084 rating_html: ratingHtml, 1114 1085 review_count: product.review_count || product.rating_count || 0, 1115 1086 1116 1087 // Availability 1117 1088 in_stock: product.in_stock !== false && product.stock_status !== 'outofstock', 1118 1089 stock_status: product.stock_status || (product.in_stock !== false ? 'instock' : 'outofstock'), 1119 1090 availability_text: product.availability_text || (product.in_stock !== false ? 'In Stock' : 'Out of Stock'), 1120 1091 1121 1092 // Additional info 1122 1093 short_description: product.short_description || product.excerpt || product.description || '', … … 1125 1096 badge: product.badge || (salePrice && parseFloat(salePrice) < parseFloat(price) ? 'Sale' : ''), 1126 1097 }; 1127 1128 console.log('🔧 Normalized product:', normalizedProduct.name, { 1129 price_html: normalizedProduct.price_html, 1130 rating_html: normalizedProduct.rating_html, 1131 availability_text: normalizedProduct.availability_text 1132 }); 1133 1098 1134 1099 return normalizedProduct; 1135 1100 } … … 1425 1390 1426 1391 if (typeof content !== 'string') return content; 1427 1428 console.log('🧹 Cleaning message content. Original length:', content.length); 1429 1392 1430 1393 // Remove ALL product card HTML completely - use more aggressive regex 1431 1394 // This regex will match everything from opening <div class="product-card to the last </div> … … 1449 1412 // Clean whitespace and normalize 1450 1413 content = content.replace(/\s+/g, ' ').replace(/\s*\n\s*/g, ' ').trim(); 1451 1452 console.log('✅ Cleaned message content. New length:', content.length); 1453 console.log('🔍 Final content:', content.substring(0, 200) + '...'); 1454 1414 1455 1415 return content; 1456 1416 } … … 1516 1476 audio.play().catch(e => { 1517 1477 // Ignore autoplay policy errors 1518 console.log('Could not play sound:', e.message);1519 1478 }); 1520 1479 } catch (error) { 1521 console.log('Sound playback failed:', error.message);1522 1480 } 1523 1481 } … … 1816 1774 limit: Math.round(performance.memory.jsHeapSizeLimit / 1048576) 1817 1775 }; 1818 1819 if (memoryInfo.used > 50) { // 50MB threshold1820 console.warn('High memory usage detected:', memoryInfo);1821 }1822 1776 }, 30000); 1823 1777 } … … 1866 1820 1867 1821 handleSessionInfo(data) { 1868 console.log('Session info received:', data);1869 1822 // Update UI with session information 1870 1823 } … … 1945 1898 1946 1899 if (sessionAge < maxAge) { 1947 console.log('🔄 Loading existing session:', session.sessionId);1948 1900 this.sessionId = session.sessionId; 1949 1901 this.messageHistory = session.messages || []; … … 1953 1905 return true; 1954 1906 } else { 1955 console.log('⏰ Session expired, creating new one');1956 1907 sessionStorage.removeItem(this.sessionKey); 1957 1908 } … … 1960 1911 // Create new session 1961 1912 this.sessionId = this.generateSessionId(); 1962 console.log('🆕 Creating new session:', this.sessionId);1963 1913 return false; 1964 1914 1965 1915 } catch (error) { 1966 console.error('❌ Error loading session:', error);1967 1916 this.sessionId = this.generateSessionId(); 1968 1917 return false; … … 1980 1929 1981 1930 sessionStorage.setItem(this.sessionKey, JSON.stringify(sessionData)); 1982 console.log('💾 Session saved:', this.sessionId);1983 1931 } catch (error) { 1984 console.error('❌ Error saving session:', error);1985 1932 } 1986 1933 }, … … 1988 1935 restoreMessages() { 1989 1936 if (this.messageHistory.length > 0) { 1990 console.log('📱 Restoring', this.messageHistory.length, 'messages');1991 1992 1937 // Clear current messages 1993 1938 $('#chatbot-messages').empty(); … … 2017 1962 this.preferences = stored ? JSON.parse(stored) : {}; 2018 1963 } catch (error) { 2019 console.warn('Failed to load preferences:', error);2020 1964 this.preferences = {}; 2021 1965 } … … 2039 1983 this.storage.setItem(this.prefix + 'preferences', JSON.stringify(this.preferences)); 2040 1984 } catch (error) { 2041 console.warn('Failed to save preferences:', error);2042 1985 } 2043 1986 } … … 2521 2464 localStorage.setItem('convertybot_coupons', JSON.stringify(couponsData)); 2522 2465 } catch (error) { 2523 console.warn('Failed to save coupons:', error);2524 2466 } 2525 2467 } … … 2540 2482 } 2541 2483 } catch (error) { 2542 console.warn('Failed to load saved coupons:', error);2543 2484 } 2544 2485 } -
convertybot/trunk/assets/js/frontend.js
r3445596 r3445725 6 6 'use strict'; 7 7 8 // ✅ FIX Bug 6: Disable console logs in production mode 9 // Set convertyBotConfig.debug = true to enable logs 10 const isDebugMode = typeof convertyBotConfig !== 'undefined' && convertyBotConfig.debug === true; 11 if (!isDebugMode) { 12 // Store original console methods for potential restoration 13 const originalConsole = { 14 log: console.log, 15 warn: console.warn, 16 info: console.info, 17 debug: console.debug 18 }; 19 // Silence non-error logs in production 20 console.log = function() {}; 21 console.info = function() {}; 22 console.debug = function() {}; 23 // Keep console.warn and console.error for important issues 24 // Restore console for debugging: window.restoreConsole = function() { Object.assign(console, originalConsole); } 25 window._originalConsole = originalConsole; 26 } 27 28 // ✅ FIX: Track page load time for engagement tracking 8 // Track page load time for engagement tracking 29 9 if (!window.pageLoadTime) { 30 10 window.pageLoadTime = Date.now(); 31 console.log('⏱️ Page load time tracked:', window.pageLoadTime);32 11 } 33 12 … … 44 23 // Initialize the chat 45 24 init: function() { 46 console.log('Initializing ConvertyBot Chatbot...');47 48 25 // Check if config exists 49 26 if (typeof convertyBotConfig === 'undefined') { … … 56 33 if (window.convertyBotGuestId) { 57 34 this.guestId = window.convertyBotGuestId; 58 console.log('👤 Guest ID (from global):', this.guestId);59 35 } else { 60 36 this.guestId = this.getOrCreateGuestId(); 61 console.log('👤 Guest ID (created):', this.guestId);62 37 } 63 38 … … 100 75 } 101 76 102 // ✅CRITICAL: Check if guest needs migration to logged-in user77 // CRITICAL: Check if guest needs migration to logged-in user 103 78 this.checkAndTriggerMigration(); 104 105 console.log('ConvertyBot Chatbot initialized successfully');106 79 }, 107 80 … … 119 92 const guestId = this.getCookie('convertybot_guest_id'); 120 93 if (!guestId) { 121 console.log('No guest ID cookie found, migration not needed');122 94 return; 123 95 } … … 126 98 const migrationKey = 'convertybot_migrated_' + convertyBotConfig.userId; 127 99 if (localStorage.getItem(migrationKey) === guestId) { 128 console.log('Migration already completed for this user/guest combination');129 100 return; 130 101 } 131 132 console.log('🔄 Triggering guest-to-user migration...', {133 guestId: guestId,134 userId: convertyBotConfig.userId135 });136 102 137 103 // Trigger migration via AJAX … … 151 117 success: (response) => { 152 118 if (response.success) { 153 console.log('✅ Guest data migration successful:', response.data);154 119 // Mark migration as done 155 120 localStorage.setItem(migrationKey, guestId); 156 121 // Note: Backend MongoDB migration is handled by the PHP AJAX handler 157 122 } else { 158 console.error(' ❌Guest data migration failed:', response.data);123 console.error('Guest data migration failed:', response.data); 159 124 } 160 125 }, 161 126 error: (xhr, status, error) => { 162 console.error(' ❌Guest data migration AJAX error:', error);127 console.error('Guest data migration AJAX error:', error); 163 128 } 164 129 }); … … 202 167 if (response.success && response.data.session_id) { 203 168 this.sessionId = response.data.session_id; 204 console.log('Chat session started:', this.sessionId, convertyBotConfig.isLoggedIn ? '(logged-in user)' : '(guest)');205 169 // Save the session with the new backend session ID 206 170 this.saveSession(); … … 211 175 } 212 176 }); 213 } else {214 console.log('Using existing session:', this.sessionId);215 177 } 216 178 }, … … 228 190 e.preventDefault(); 229 191 e.stopPropagation(); 230 console.log('Close button clicked');231 192 self.closeChat(); 232 193 }); … … 236 197 e.preventDefault(); 237 198 e.stopPropagation(); 238 console.log('Minimize button clicked');239 199 self.closeChat(); 240 200 }); … … 264 224 // Save session before navigation 265 225 ConvertyBotChat.saveSession(); 266 console.log('💾 Session saved before product navigation');267 226 window.location.href = productUrl; // Open in same page 268 227 } … … 288 247 // Save session (non-blocking) 289 248 ConvertyBotChat.saveSession(); 290 console.log('💾 Session saved, opening in new tab');291 249 292 250 // Reset after 1 second … … 336 294 // Save session (non-blocking) 337 295 ConvertyBotChat.saveSession(); 338 console.log('💾 Session saved, opening in new tab from modal');339 296 340 297 // Reset after 1 second … … 393 350 // Open chat window 394 351 openChat: function() { 395 console.log('Opening chat...');396 352 const widget = $('#convertybot-widget'); 397 353 const chatWindow = $('#convertybot-chat-window'); … … 420 376 // Close chat window 421 377 closeChat: function() { 422 console.log('Closing chat...');423 378 const widget = $('#convertybot-widget'); 424 379 const chatWindow = $('#convertybot-chat-window'); … … 438 393 // Track event 439 394 this.trackEvent('chat_closed'); 440 console.log('Chat closed, button shown');441 395 }, 442 396 … … 467 421 this.showTyping(); 468 422 469 // ✅ FIX:Collect engagement data before sending423 // Collect engagement data before sending 470 424 const engagementData = this.collectEngagementData(); 471 472 console.log('📊 Engagement data collected:', engagementData);473 425 474 426 // Send to backend … … 511 463 this.hideTyping(); 512 464 513 console.log('========================================');514 console.log('🔍 RESPONSE RECEIVED FROM BACKEND');515 console.log('========================================');516 console.log('Full response:', response);517 console.log('response.success:', response.success);518 console.log('response.data exists?', !!response.data);519 520 465 if (response.success && response.data) { 521 console.log('✅ Response is successful and has data');522 console.log('response.data.ai_response exists?', !!response.data.ai_response);523 524 if (response.data.ai_response) {525 console.log('📊 AI Response structure:');526 console.log('- message exists?', !!response.data.ai_response.message);527 console.log('- products exists?', !!response.data.ai_response.products);528 console.log('- coupons exists?', !!response.data.ai_response.coupons);529 console.log('- metadata exists?', !!response.data.ai_response.metadata);530 }531 532 466 // Add AI response 533 467 if (response.data.ai_response && response.data.ai_response.message) { … … 536 470 if (response.data.ai_response.products && response.data.ai_response.products.length > 0) { 537 471 products = response.data.ai_response.products; 538 console.log('📦 Products found (direct path):', products.length);539 472 } else if (response.data.ai_response.metadata && response.data.ai_response.metadata.products) { 540 473 products = response.data.ai_response.metadata.products; 541 console.log('📦 Products found (metadata):', products.length);542 474 } 543 475 544 476 // Extract coupons from response (check direct path first, then metadata) 545 console.log('🎟️ CHECKING FOR COUPONS...');546 console.log('- response.data.ai_response.coupons:', response.data.ai_response.coupons);547 console.log('- Is array?', Array.isArray(response.data.ai_response.coupons));548 console.log('- Length:', response.data.ai_response.coupons ? response.data.ai_response.coupons.length : 0);549 550 477 let coupons = null; 551 478 if (response.data.ai_response.coupons && response.data.ai_response.coupons.length > 0) { 552 479 coupons = response.data.ai_response.coupons; 553 console.log('✅ 🎟️ Coupons received (DIRECT PATH):', coupons.length);554 console.log('Coupon details:', coupons);555 480 } else if (response.data.ai_response.metadata && response.data.ai_response.metadata.coupons && response.data.ai_response.metadata.coupons.length > 0) { 556 481 coupons = response.data.ai_response.metadata.coupons; 557 console.log('✅ 🎟️ Coupons received (METADATA PATH):', coupons.length);558 console.log('Coupon details:', coupons);559 } else {560 console.warn('⚠️ NO COUPONS FOUND in response');561 console.log('Checked paths:');562 console.log('1. response.data.ai_response.coupons:', response.data.ai_response.coupons);563 console.log('2. response.data.ai_response.metadata.coupons:', response.data.ai_response.metadata?.coupons);564 482 } 565 483 566 484 // Store products BEFORE adding message (critical for persistence) 567 485 if (products && products.length > 0) { 568 console.log('Storing products for persistence:', products.length);569 486 this.lastProductsData = products; 570 487 this.allProducts = products; … … 574 491 } 575 492 576 // ✅ FIX:Store coupons BEFORE adding message (for persistence like products)493 // Store coupons BEFORE adding message (for persistence like products) 577 494 if (coupons && coupons.length > 0) { 578 console.log('Storing coupons for persistence:', coupons.length);579 495 this.lastCouponsData = coupons; 580 496 } else { … … 586 502 587 503 // Show coupons inline if available (show BEFORE products for better visibility) 588 console.log('🎯 About to display coupons...');589 console.log('- coupons variable:', coupons);590 console.log('- coupons is truthy?', !!coupons);591 504 if (coupons) { 592 console.log('✅ Calling showCoupons() with', coupons.length, 'coupons');593 505 this.showCoupons(coupons); 594 console.log('✅ showCoupons() called successfully');595 } else {596 console.warn('❌ NOT calling showCoupons() because coupons variable is', coupons);597 506 } 598 507 599 508 // Show products inline if available 600 509 if (products) { 601 console.log('Showing products:', products.length);602 510 this.showProducts(products); 603 511 } 604 605 console.log('========================================');606 console.log('✅ RESPONSE PROCESSING COMPLETE');607 console.log('========================================');608 512 609 513 // Add suggestions if available … … 709 613 showProducts: function(products) { 710 614 if (!products || products.length === 0) { 711 console.log('⚠️ No products to show');712 615 return; 713 616 } 714 715 console.log('📦 Showing products:', products.length, 'total');716 617 717 618 // Store all products for modal and for persistence … … 724 625 725 626 if (messageBubble.length === 0) { 726 console.log('⚠️ No message bubble found');727 627 return; 728 628 } … … 730 630 // Show only first 3 products inline 731 631 const displayProducts = products.slice(0, 3); 732 console.log('📦 Displaying', displayProducts.length, 'products inline');733 632 734 633 // Create products HTML to insert INSIDE the message bubble … … 767 666 // Add "See More Products" button if there are more than 3 products 768 667 if (products.length > 3) { 769 console.log('✅ Adding "Voir plus" button for', products.length - 3, 'more products');770 668 productsHtml += ` 771 669 <div class="ai-more-products-section"> … … 778 676 </div> 779 677 `; 780 } else {781 console.log('ℹ️ Only', products.length, 'products, no "Voir plus" button needed');782 678 } 783 679 … … 872 768 // Show coupons 873 769 showCoupons: function(coupons) { 874 console.log('========================================');875 console.log('🎟️ showCoupons() FUNCTION CALLED');876 console.log('========================================');877 console.log('Coupons parameter:', coupons);878 console.log('Coupons type:', typeof coupons);879 console.log('Is array?', Array.isArray(coupons));880 console.log('Length:', coupons ? coupons.length : 'N/A');881 882 770 if (!coupons || coupons.length === 0) { 883 console.warn('❌ No coupons to show (empty or null)');884 771 return; 885 772 } 886 773 887 console.log('✅ Coupons validation passed, proceeding with', coupons.length, 'coupon(s)');888 889 774 // Find the latest bot message 890 console.log('🔍 Looking for latest bot message...');891 775 const lastBotMessage = $('.convertybot-message.bot').last(); 892 console.log('Last bot message found?', lastBotMessage.length > 0);893 894 776 const messageBubble = lastBotMessage.find('.message-bubble'); 895 console.log('Message bubble found?', messageBubble.length > 0);896 777 897 778 if (messageBubble.length === 0) { 898 console.error(' ❌No message bubble found for coupon - cannot display');779 console.error('No message bubble found for coupon - cannot display'); 899 780 return; 900 781 } 901 782 902 console.log('✅ Message bubble ready, creating coupon HTML...'); 903 904 // ✅ FIX: Use shared generateCouponHtml function to avoid code duplication 783 // Use shared generateCouponHtml function to avoid code duplication 905 784 const couponsHtml = this.generateCouponHtml(coupons); 906 785 907 console.log('📝 Coupon HTML created, length:', couponsHtml.length, 'characters');908 console.log('HTML preview:', couponsHtml.substring(0, 200) + '...');909 910 786 // Append coupon HTML INSIDE the message bubble 911 console.log('📌 Appending coupon HTML to message bubble...');912 787 messageBubble.append(couponsHtml); 913 console.log('✅ Coupon HTML appended successfully');914 788 915 789 // Scroll to show new content 916 790 const messagesContainer = $('#convertybot-chat-messages'); 917 791 messagesContainer.scrollTop(messagesContainer[0].scrollHeight); 918 919 console.log('========================================');920 console.log('✅ COUPONS DISPLAYED SUCCESSFULLY');921 console.log('========================================');922 792 }, 923 793 … … 926 796 if (navigator.clipboard && navigator.clipboard.writeText) { 927 797 navigator.clipboard.writeText(code).then(() => { 928 console.log('✅ Coupon code copied:', code);929 798 // Show a brief success message 930 799 const btn = $(`.coupon-copy-btn[data-code="${code}"]`); … … 955 824 try { 956 825 document.execCommand('copy'); 957 console.log('✅ Coupon code copied (fallback):', code);958 826 } catch (err) { 959 827 console.error('Failed to copy:', err); … … 965 833 addToCart: function(productId) { 966 834 // This would integrate with WooCommerce cart 967 console.log('Adding product to cart:', productId);968 835 // You can customize this based on your e-commerce platform 969 836 }, … … 1011 878 audio.volume = convertyBotConfig.soundVolume || 0.3; 1012 879 audio.play().catch(() => { 1013 console.debug('Notification sound disabled - could not play');1014 880 convertyBotConfig.soundEnabled = false; 1015 881 }); 1016 882 } catch (fallbackError) { 1017 console.debug('Audio notifications not supported');1018 883 convertyBotConfig.soundEnabled = false; 1019 884 } … … 1211 1076 guestId = 'guest_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); 1212 1077 localStorage.setItem(GUEST_ID_KEY, guestId); 1213 console.log('🆕 Created new persistent guest ID:', guestId);1214 } else {1215 console.log('📌 Using existing guest ID:', guestId);1216 1078 } 1217 1079 … … 1230 1092 secureFlag; 1231 1093 1232 console.log('🍪 Guest ID saved to cookie for PHP access (HTTPS: ' + isHttps + ')');1233 1234 1094 return guestId; 1235 1095 } catch (error) { 1236 1096 // Fallback if localStorage is not available 1237 console.warn('localStorage not available, using session-based guest ID');1238 1097 return 'guest_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); 1239 1098 } … … 1270 1129 } 1271 1130 } catch (e) { 1272 console.warn('Could not get engagement SDK data:', e); 1273 } 1274 } 1275 1276 console.log('📊 Collected engagement data:', data); 1131 // Silently handle SDK data retrieval errors 1132 } 1133 } 1134 1277 1135 return data; 1278 1136 }, 1279 1137 1280 // ✅ FIX:Detect WooCommerce product from page1138 // Detect WooCommerce product from page 1281 1139 detectWooCommerceProduct: function() { 1282 1140 // Check if we're on a product page 1283 1141 const productElement = $('.product, .single-product, .woocommerce-product'); 1284 1142 if (productElement.length === 0) { 1285 console.log('❌ Not a product page');1286 1143 return null; 1287 1144 } … … 1294 1151 1295 1152 if (!productName) { 1296 console.log('❌ Could not detect product name');1297 1153 return null; 1298 1154 } … … 1306 1162 }; 1307 1163 1308 console.log('✅ Product detected:', product);1309 1164 return product; 1310 1165 }, … … 1345 1200 const session = this.decryptData(sessionData); 1346 1201 if (!session) { 1347 console.log('❌ Failed to decrypt session, creating new one');1348 1202 localStorage.removeItem(this.sessionKey); 1349 1203 return false; … … 1363 1217 if (inactivityTime >= sessionTimeout && browserWasClosed) { 1364 1218 // Session expired AND browser was closed - start fresh 1365 console.log('🆕 New browser session detected (timeout + browser was closed)');1366 1219 localStorage.removeItem(this.sessionKey); 1367 1220 return false; … … 1370 1223 if (inactivityTime < sessionTimeout) { 1371 1224 // Session is still valid - load it (works across tabs!) 1372 console.log('🔄 Loading existing session:', session.sessionId, '(shared across tabs)');1373 1225 this.sessionId = session.sessionId; 1374 1226 this.messageHistory = session.messages || []; … … 1380 1232 // Session timed out but browser wasn't closed 1381 1233 // This means user was inactive for 30+ min but kept browser open 1382 console.log('⏰ Session expired (inactive for', Math.round(inactivityTime / 60000), 'minutes)');1383 1234 localStorage.removeItem(this.sessionKey); 1384 1235 return false; … … 1387 1238 1388 1239 // No existing session found 1389 console.log('🆕 No existing session found, will create new one');1390 1240 return false; 1391 1241 1392 1242 } catch (error) { 1393 console.error(' ❌Error loading session:', error);1243 console.error('Error loading session:', error); 1394 1244 return false; 1395 1245 } … … 1420 1270 if (encryptedData) { 1421 1271 localStorage.setItem(this.sessionKey, encryptedData); 1422 console.log('🔒 Session saved (shared across tabs):', this.sessionId);1423 1272 } else { 1424 console.error(' ❌Failed to encrypt session data');1273 console.error('Failed to encrypt session data'); 1425 1274 } 1426 1275 } catch (error) { 1427 console.error(' ❌Error saving session:', error);1276 console.error('Error saving session:', error); 1428 1277 } 1429 1278 }, … … 1431 1280 restoreMessages: function() { 1432 1281 if (this.messageHistory.length > 0) { 1433 console.log('📱 Restoring', this.messageHistory.length, 'messages');1434 1282 1435 1283 // Clear current messages … … 1449 1297 createNewLocalSession: function() { 1450 1298 this.sessionId = 'sess_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); 1451 console.log('🆕 Created new local session:', this.sessionId);1452 1299 this.saveSession(); 1453 1300 }, … … 1477 1324 const messageBubble = justAddedMessage.find('.message-bubble'); 1478 1325 1479 // ✅ FIX:If message has coupons, render them DIRECTLY into the message bubble1326 // If message has coupons, render them DIRECTLY into the message bubble 1480 1327 // (Don't use showCoupons which uses a general selector that might fail during rapid restoration) 1481 1328 if (message.coupons && message.coupons.length > 0 && messageBubble.length > 0) { 1482 console.log('📱 Restoring coupons directly:', message.coupons.length);1483 1329 const couponHtml = this.generateCouponHtml(message.coupons); 1484 1330 messageBubble.append(couponHtml); -
convertybot/trunk/assets/js/guest-id-cookie.js
r3437100 r3445725 47 47 // Verify cookie was set 48 48 const verification = getCookie(name); 49 if (verification === value) { 50 console.log('[Guest ID] ✅ Cookie set successfully:', value); 51 return true; 52 } else { 53 console.error('[Guest ID] ❌ Failed to set cookie. Got:', verification); 54 return false; 55 } 49 return verification === value; 56 50 } 57 51 … … 65 59 66 60 if (guestId) { 67 console.log('[Guest ID] 📌 Found in cookie:', guestId);68 69 61 // Also sync to localStorage 70 62 try { 71 63 localStorage.setItem(GUEST_ID_KEY, guestId); 72 64 } catch (e) { 73 console.warn('[Guest ID] Could not sync to localStorage:', e);65 // Silent fail for localStorage 74 66 } 75 67 … … 81 73 guestId = localStorage.getItem(GUEST_ID_KEY); 82 74 if (guestId) { 83 console.log('[Guest ID] 📦 Found in localStorage:', guestId);84 75 // Create cookie from localStorage value 85 76 setCookie(COOKIE_NAME, guestId, 365); … … 87 78 } 88 79 } catch (e) { 89 console.warn('[Guest ID] Could not access localStorage:', e);80 // Silent fail for localStorage 90 81 } 91 82 92 83 // Step 3: Generate new guest ID 93 84 guestId = 'guest_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); 94 console.log('[Guest ID] 🆕 Created new ID:', guestId);95 85 96 86 // Save to both cookie and localStorage 97 const cookieSuccess =setCookie(COOKIE_NAME, guestId, 365);87 setCookie(COOKIE_NAME, guestId, 365); 98 88 99 89 try { 100 90 localStorage.setItem(GUEST_ID_KEY, guestId); 101 91 } catch (e) { 102 console.warn('[Guest ID] Could not save to localStorage:', e); 103 } 104 105 if (!cookieSuccess) { 106 console.error('[Guest ID] ⚠️ WARNING: Cookie creation failed! Purchase tracking may not work!'); 92 // Silent fail for localStorage 107 93 } 108 94 … … 110 96 111 97 } catch (error) { 112 console.error('[Guest ID] Critical error:', error);113 98 // Return a session-based fallback 114 99 return 'guest_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); … … 126 111 const cookieValue = getCookie(COOKIE_NAME); 127 112 if (cookieValue !== guestId) { 128 console.warn('[Guest ID] Cookie mismatch, forcing recreate...');129 113 setCookie(COOKIE_NAME, guestId, 365); 130 114 } … … 132 116 // Make guest ID globally available 133 117 window.convertyBotGuestId = guestId; 134 135 console.log('[Guest ID] ✅ Ready:', guestId);136 118 } 137 119 -
convertybot/trunk/assets/js/migration-handler.js
r3437100 r3445725 8 8 9 9 var MigrationHandler = { 10 debug: true,11 12 log: function(message, data) {13 if (this.debug) {14 if (data) {15 console.log('[ConvertyBot Migration] ' + message, data);16 } else {17 console.log('[ConvertyBot Migration] ' + message);18 }19 }20 },21 22 error: function(message, data) {23 console.error('[ConvertyBot Migration ERROR] ' + message, data || '');24 },25 26 10 init: function() { 27 this.log('========================================');28 this.log('Migration Handler Initialized');29 this.log('========================================');30 31 11 // Check if config is available 32 12 if (typeof convertyBotConfig === 'undefined') { 33 this.log('Config not available, waiting...');34 13 return; 35 14 } 36 15 37 this.log('User logged in: ' + convertyBotConfig.isLoggedIn);38 this.log('User ID: ' + convertyBotConfig.userId);39 this.log('User Email: ' + convertyBotConfig.userEmail);40 this.log('User Name: ' + convertyBotConfig.userName);41 42 16 // Only proceed if user is logged in 43 17 if (!convertyBotConfig.isLoggedIn || !convertyBotConfig.userId) { 44 this.log('User not logged in, migration not needed');45 18 return; 46 19 } … … 48 21 // Get guest ID from multiple sources 49 22 var guestId = this.getGuestId(); 50 this.log('Guest ID found: ' + guestId);51 23 52 24 if (!guestId) { 53 this.log('No guest ID found, nothing to migrate');54 25 return; 55 26 } … … 59 30 var alreadyMigrated = localStorage.getItem(migrationKey); 60 31 61 this.log('Migration key: ' + migrationKey);62 this.log('Already migrated: ' + alreadyMigrated);63 64 32 if (alreadyMigrated === 'success') { 65 this.log('Migration already completed successfully');66 33 return; 67 34 } 68 35 69 36 // Force migration 70 this.log('🔄 Starting forced migration...');71 37 this.triggerMigration(guestId, migrationKey); 72 38 }, … … 76 42 var guestId = localStorage.getItem('convertybot_guest_id'); 77 43 if (guestId) { 78 this.log('Found guestId in localStorage: ' + guestId);79 44 return guestId; 80 45 } … … 83 48 guestId = this.getCookie('convertybot_guest_id'); 84 49 if (guestId) { 85 this.log('Found guestId in cookie: ' + guestId);86 50 return guestId; 87 51 } … … 90 54 guestId = sessionStorage.getItem('convertybot_guest_id'); 91 55 if (guestId) { 92 this.log('Found guestId in sessionStorage: ' + guestId);93 56 return guestId; 94 57 } … … 113 76 }; 114 77 115 this.log('Migration data:', migrationData);116 117 78 $.ajax({ 118 79 url: convertyBotConfig.ajaxUrl, … … 124 85 }, 125 86 success: function(response) { 126 self.log('Migration response:', response);127 128 87 if (response.success) { 129 self.log('✅ Migration successful!', response.data);130 88 // Mark as migrated 131 89 localStorage.setItem(migrationKey, 'success'); … … 134 92 self.triggerBackendMigration(guestId, migrationData); 135 93 } else { 136 self.error('Migration failed:', response.data);137 94 localStorage.setItem(migrationKey, 'failed'); 138 95 } 139 96 }, 140 97 error: function(xhr, status, error) { 141 self.error('Migration AJAX error:', {142 status: status,143 error: error,144 response: xhr.responseText145 });146 98 localStorage.setItem(migrationKey, 'error'); 147 99 } … … 150 102 151 103 triggerBackendMigration: function(guestId, migrationData) { 152 var self = this;153 154 104 // Also call the backend API directly 155 105 if (!convertyBotConfig.apiUrl) { 156 this.log('No API URL configured, skipping direct backend call');157 106 return; 158 107 } … … 160 109 var apiKey = this.getApiKey(); 161 110 if (!apiKey) { 162 this.log('No API key available, skipping direct backend call');163 111 return; 164 112 } 165 166 this.log('Calling backend API directly...');167 113 168 114 $.ajax({ … … 183 129 last_name: '' 184 130 } 185 }), 186 success: function(response) { 187 self.log('Backend API migration response:', response); 188 }, 189 error: function(xhr, status, error) { 190 self.log('Backend API error (non-critical):', error); 191 } 131 }) 192 132 }); 193 133 }, -
convertybot/trunk/assets/js/product-cards-handler.js
r3437100 r3445725 115 115 // Compatibility - ensure products are stored for modal use 116 116 document.addEventListener('DOMContentLoaded', function() { 117 if (window.ConvertyBotChat) { 118 console.log('Product cards handler initialized'); 119 } 117 // Handler initialized 120 118 }); 121 119 -
convertybot/trunk/assets/js/product-showcase-enhanced.js
r3437100 r3445725 69 69 this.initializePersonalization(); 70 70 this.setupRealtimeConnection(); 71 72 console.log('Enhanced Product Showcase initialized');73 71 } 74 72 … … 1073 1071 handleQuickView(productId) { 1074 1072 // Show quick view modal (would need to be implemented) 1075 console.log('Quick view for product:', productId); 1076 1073 1077 1074 // Analytics 1078 1075 if (window.aiTrack) { -
convertybot/trunk/assets/js/service-worker.js
r3437100 r3445725 40 40 */ 41 41 self.addEventListener('install', (event) => { 42 console.log('Service Worker: Installing...');43 44 42 event.waitUntil( 45 43 caches.open(CACHE_NAME) 46 44 .then((cache) => { 47 console.log('Service Worker: Caching static assets');48 45 return cache.addAll(STATIC_CACHE_ASSETS); 49 46 }) 50 47 .then(() => { 51 console.log('Service Worker: Installation complete');52 48 return self.skipWaiting(); 53 49 }) … … 62 58 */ 63 59 self.addEventListener('activate', (event) => { 64 console.log('Service Worker: Activating...');65 66 60 event.waitUntil( 67 61 Promise.all([ … … 72 66 .filter((cacheName) => cacheName !== CACHE_NAME) 73 67 .map((cacheName) => { 74 console.log('Service Worker: Deleting old cache', cacheName);75 68 return caches.delete(cacheName); 76 69 }) … … 79 72 // Take control of all clients 80 73 self.clients.claim() 81 ]).then(() => { 82 console.log('Service Worker: Activation complete'); 83 }) 74 ]) 84 75 ); 85 76 }); … … 129 120 return networkResponse; 130 121 } catch (error) { 131 console.log('Service Worker: Network failed, trying cache', error);132 133 122 const cachedResponse = await caches.match(request); 134 123 if (cachedResponse) { … … 169 158 return networkResponse; 170 159 } catch (error) { 171 console.error('Service Worker: Cache-first failed', error);172 160 throw error; 173 161 } … … 189 177 return networkResponse; 190 178 }).catch((error) => { 191 console.log('Service Worker: Network update failed', error);179 // Network update failed 192 180 }); 193 181 … … 221 209 self.addEventListener('sync', (event) => { 222 210 if (event.tag === 'background-message-sync') { 223 console.log('Service Worker: Background sync triggered');224 211 event.waitUntil(syncPendingMessages()); 225 212 } … … 233 220 // Get pending messages from IndexedDB 234 221 const pendingMessages = await getPendingMessages(); 235 222 236 223 if (pendingMessages.length === 0) { 237 console.log('Service Worker: No pending messages to sync');238 224 return; 239 225 } 240 241 console.log(`Service Worker: Syncing ${pendingMessages.length} pending messages`); 242 226 243 227 // Send each message 244 228 for (const message of pendingMessages) { … … 249 233 body: message.body 250 234 }); 251 235 252 236 if (response.ok) { 253 237 await removePendingMessage(message.id); 254 console.log('Service Worker: Message synced successfully', message.id);255 } else {256 console.error('Service Worker: Failed to sync message', message.id, response.status);257 238 } 258 239 } catch (error) { 259 console.error('Service Worker: Error syncing message', message.id, error);260 } 261 } 262 240 // Error syncing message 241 } 242 } 243 263 244 // Notify clients about sync completion 264 245 const clients = await self.clients.matchAll(); … … 269 250 }); 270 251 }); 271 252 272 253 } catch (error) { 273 254 console.error('Service Worker: Background sync failed', error); … … 279 260 */ 280 261 self.addEventListener('push', (event) => { 281 console.log('Service Worker: Push notification received');282 283 262 const options = { 284 263 body: 'You have a new message from AI Assistant', … … 311 290 } 312 291 } catch (error) { 313 console.error('Service Worker: Error parsing push data', error);292 // Error parsing push data 314 293 } 315 294 } … … 324 303 */ 325 304 self.addEventListener('notificationclick', (event) => { 326 console.log('Service Worker: Notification clicked', event.action);327 328 305 event.notification.close(); 329 306 … … 383 360 }); 384 361 } catch (error) { 385 console.error('Service Worker: Error getting pending messages', error);386 362 return []; 387 363 } … … 400 376 }); 401 377 } catch (error) { 402 console.error('Service Worker: Error removing pending message', error);378 // Error removing pending message 403 379 } 404 380 } … … 426 402 427 403 default: 428 console.log('Service Worker: Unknown message type', type);404 // Unknown message type 429 405 } 430 406 }); … … 437 413 const cache = await caches.open(CACHE_NAME); 438 414 await cache.addAll(urls); 439 console.log('Service Worker: URLs cached successfully', urls); 440 } catch (error) { 441 console.error('Service Worker: Error caching URLs', error); 415 } catch (error) { 416 // Error caching URLs 442 417 } 443 418 } … … 452 427 cacheNames.map(cacheName => caches.delete(cacheName)) 453 428 ); 454 console.log('Service Worker: All caches cleared'); 455 } catch (error) { 456 console.error('Service Worker: Error clearing caches', error); 429 } catch (error) { 430 // Error clearing caches 457 431 } 458 432 } … … 475 449 if (age > MAX_AGE) { 476 450 cache.delete(request); 477 console.log('Service Worker: Deleted old cached response', request.url);478 451 } 479 452 } … … 492 465 */ 493 466 self.addEventListener('online', () => { 494 console.log('Service Worker: Connection restored');495 467 // Trigger background sync when connection is restored 496 468 self.registration.sync.register('background-message-sync'); … … 498 470 499 471 self.addEventListener('offline', () => { 500 console.log('Service Worker: Connection lost'); 501 }); 502 503 console.log('Service Worker: Script loaded successfully'); 472 // Connection lost 473 }); -
convertybot/trunk/assets/js/tracking.js
r3437100 r3445725 10 10 // Check if tracking config exists 11 11 if (typeof convertyBotTracking === 'undefined') { 12 console.log('[ConvertyBot Tracking] Config not available, skipping basic tracking');13 12 return; 14 13 } … … 25 24 this.initialized = true; 26 25 27 console.log('[ConvertyBot Tracking] Initializing basic tracking...');28 29 26 // Get session ID from cookie or storage 30 27 this.sessionId = this.getSessionId(); 31 28 32 29 if (!this.sessionId) { 33 console.log('[ConvertyBot Tracking] No session ID found, waiting for chatbot to initialize');34 30 // Listen for chatbot session 35 31 $(document).on('convertybot_session_started', function(e, data) { … … 47 43 this.setupClickTracking(); 48 44 this.setupTimeTracking(); 49 50 console.log('[ConvertyBot Tracking] Basic tracking initialized', {51 sessionId: this.sessionId,52 pageType: convertyBotTracking.currentPage?.type53 });54 45 }, 55 46 … … 206 197 url: convertyBotTracking.ajaxUrl, 207 198 type: 'POST', 208 data: data, 209 success: function(response) { 210 if (response.success) { 211 console.log('[ConvertyBot Tracking] ' + action + ' tracked successfully'); 212 } 213 }, 214 error: function(xhr, status, error) { 215 console.warn('[ConvertyBot Tracking] Failed to track ' + action, error); 216 } 199 data: data 217 200 }); 218 201 } -
convertybot/trunk/convertybot.php
r3445596 r3445725 3 3 * Plugin Name: ConvertyBot 4 4 * Description: An intelligent AI-powered sales assistant for WooCommerce that helps visitors discover products, provides personalized recommendations, and generates dynamic discount codes to boost sales. 5 * Version: 1.0.2 15 * Version: 1.0.28 6 6 * Author: ConvertyBot, 2wstechnologies Team 7 7 * Author URI: https://convertybot.com … … 42 42 43 43 // Define plugin constants 44 define('CONVERTYBOT_VERSION', '1.0.2 6');44 define('CONVERTYBOT_VERSION', '1.0.28'); 45 45 define('CONVERTYBOT_PLUGIN_URL', plugin_dir_url(__FILE__)); 46 46 define('CONVERTYBOT_PLUGIN_PATH', plugin_dir_path(__FILE__)); -
convertybot/trunk/readme.txt
r3445596 r3445725 6 6 Tested up to: 6.9 7 7 Requires PHP: 7.2 8 Stable tag: 1.0.2 68 Stable tag: 1.0.28 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 249 249 == Changelog == 250 250 251 = 1.0.28 = 252 * FIXED: Insight badges showing empty in visitor journeys timeline ({{this}} template replacement bug) 253 * FIXED: Simple variable replacement was overwriting {{this}} placeholder before #each loop could process it 254 255 = 1.0.27 = 256 * FIXED: Empty yellow insight badges in visitor journeys timeline 257 * ADDED: Debug logging for insights data to diagnose data issues 258 * ADDED: Extra cleanup to remove empty timeline-insights divs 259 * IMPROVED: Better filtering of non-string and empty insights 260 251 261 = 1.0.26 = 252 262 * FIXED: Chat window not closing when minimize button clicked … … 360 370 == Upgrade Notice == 361 371 372 = 1.0.28 = 373 Fix for empty insight badges - The {{this}} template placeholder in visitor journey timelines was being replaced with empty string before the #each loop could process it. Insights now display correctly. 374 375 = 1.0.27 = 376 Debug update - Added logging to diagnose empty insight badges in visitor journeys. Check browser console for insights data. 377 362 378 = 1.0.26 = 363 379 Critical fix - Chat window can now be properly closed/minimized. Also fixes mobile font sizes and avatar positioning.
Note: See TracChangeset
for help on using the changeset viewer.