Changeset 3317904
- Timestamp:
- 06/25/2025 11:52:04 PM (9 months ago)
- Location:
- andy-votre-assistant-intelligent
- Files:
-
- 1 added
- 5 edited
- 6 copied
-
tags/2.1.0 (added)
-
tags/2.1.0/Andy (copied) (copied from andy-votre-assistant-intelligent/trunk/Andy)
-
tags/2.1.0/Andy-Wordress-Extension.php (copied) (copied from andy-votre-assistant-intelligent/trunk/Andy-Wordress-Extension.php) (6 diffs)
-
tags/2.1.0/Andy/Andy.html (copied) (copied from andy-votre-assistant-intelligent/trunk/Andy/Andy.html) (1 diff)
-
tags/2.1.0/Andy/assets/Andy-Widget/scripts/Andy.js (copied) (copied from andy-votre-assistant-intelligent/trunk/Andy/assets/Andy-Widget/scripts/Andy.js) (3 diffs)
-
tags/2.1.0/Andy/assets/Andy-Widget/styles/Andy.css (copied) (copied from andy-votre-assistant-intelligent/trunk/Andy/assets/Andy-Widget/styles/Andy.css) (4 diffs)
-
tags/2.1.0/readme.txt (copied) (copied from andy-votre-assistant-intelligent/trunk/readme.txt) (1 diff)
-
trunk/Andy-Wordress-Extension.php (modified) (6 diffs)
-
trunk/Andy/Andy.html (modified) (1 diff)
-
trunk/Andy/assets/Andy-Widget/scripts/Andy.js (modified) (3 diffs)
-
trunk/Andy/assets/Andy-Widget/styles/Andy.css (modified) (4 diffs)
-
trunk/readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
andy-votre-assistant-intelligent/tags/2.1.0/Andy-Wordress-Extension.php
r3309069 r3317904 91 91 $plugin_url = plugin_dir_url(__FILE__); 92 92 93 // Get all contact methods 94 $contact_methods = []; 95 $contact_fields = [ 96 'messenger_dm', 'tiktok_page', 'insta_page', 'whatsapp_dm', 97 'facebook_page', 'linkedin_page', 'mail', 'tel', 'adresse', 98 'contact_page', 'twitter', 'youtube' 99 ]; 100 101 foreach ($contact_fields as $field) { 102 $value = get_option('andy_contact_' . $field, ''); 103 if (!empty($value)) { 104 $contact_methods[$field] = $value; 105 } 106 } 107 93 108 // Check WP version 94 109 global $wp_version; … … 99 114 'strategy' => 'defer', 100 115 ]); 116 117 // Pass contact methods to JavaScript 118 wp_localize_script('andy-script', 'andyContactMethods', $contact_methods); 101 119 } else { 102 120 // Old WordPress way … … 414 432 'andy_injector_section' 415 433 ); 434 435 // Add Moyens de contact section 436 add_settings_section( 437 'andy_contact_section', 438 esc_html__('Moyens de contact', 'andy-votre-assistant-intelligent'), 439 'andy_contact_section_callback', 440 'andyInjector' 441 ); 442 443 // Define contact methods with their specific configurations 444 $contact_methods = [ 445 'messenger_dm' => [ 446 'label' => 'Lien Messenger DM', 447 'type' => 'url', 448 'placeholder' => 'https://m.me/votrenom', 449 'sanitize' => 'esc_url_raw' 450 ], 451 'tiktok_page' => [ 452 'label' => 'Page TikTok', 453 'type' => 'url', 454 'placeholder' => 'https://www.tiktok.com/@votrenom', 455 'sanitize' => 'esc_url_raw' 456 ], 457 'insta_page' => [ 458 'label' => 'Page Instagram', 459 'type' => 'url', 460 'placeholder' => 'https://www.instagram.com/votrenom', 461 'sanitize' => 'esc_url_raw' 462 ], 463 'whatsapp_dm' => [ 464 'label' => 'Lien WhatsApp DM', 465 'type' => 'url', 466 'placeholder' => 'https://wa.me/33612345678', 467 'sanitize' => 'esc_url_raw' 468 ], 469 'facebook_page' => [ 470 'label' => 'Page Facebook', 471 'type' => 'url', 472 'placeholder' => 'https://www.facebook.com/votrepage', 473 'sanitize' => 'esc_url_raw' 474 ], 475 'linkedin_page' => [ 476 'label' => 'Page LinkedIn', 477 'type' => 'url', 478 'placeholder' => 'https://www.linkedin.com/company/votresociete', 479 'sanitize' => 'esc_url_raw' 480 ], 481 'mail' => [ 482 'label' => 'Adresse email', 483 'type' => 'email', 484 'placeholder' => '[email protected]', 485 'sanitize' => 'sanitize_email' 486 ], 487 'tel' => [ 488 'label' => 'Numéro de téléphone', 489 'type' => 'tel', 490 'placeholder' => '+33 6 12 34 56 78', 491 'sanitize' => 'sanitize_text_field' 492 ], 493 'adresse' => [ 494 'label' => 'Adresse physique', 495 'type' => 'text', 496 'placeholder' => '123 Rue Exemple, 75000 Paris', 497 'sanitize' => 'sanitize_text_field' 498 ], 499 'contact_page' => [ 500 'label' => 'Lien page de contact', 501 'type' => 'url', 502 'placeholder' => 'https://votresite.com/contact', 503 'sanitize' => 'esc_url_raw' 504 ], 505 'twitter' => [ 506 'label' => 'Page Twitter (X)', 507 'type' => 'url', 508 'placeholder' => 'https://twitter.com/votrenom', 509 'sanitize' => 'esc_url_raw' 510 ], 511 'youtube' => [ 512 'label' => 'Chaîne YouTube', 513 'type' => 'url', 514 'placeholder' => 'https://www.youtube.com/@votrechaine', 515 'sanitize' => 'esc_url_raw' 516 ] 517 ]; 518 519 // Register and add settings fields for each contact method 520 foreach ($contact_methods as $key => $config) { 521 $option_name = 'andy_contact_' . $key; 522 523 register_setting('andyInjector', $option_name, [ 524 'sanitize_callback' => $config['sanitize'], 525 ]); 526 527 add_settings_field( 528 $option_name, 529 esc_html__($config['label'], 'andy-votre-assistant-intelligent'), 530 'andy_contact_field_render', 531 'andyInjector', 532 'andy_contact_section', 533 [ 534 'label_for' => $option_name, 535 'placeholder' => $config['placeholder'], 536 'type' => $config['type'] 537 ] 538 ); 539 } 416 540 } 417 541 … … 435 559 } 436 560 561 /** 562 * Adjust color brightness 563 */ 564 function andy_adjust_brightness($hex, $steps) { 565 // Remove # from hex code if present 566 $hex = ltrim($hex, '#'); 567 568 // Convert to RGB 569 $r = hexdec(substr($hex, 0, 2)); 570 $g = hexdec(substr($hex, 2, 2)); 571 $b = hexdec(substr($hex, 4, 2)); 572 573 // Adjust brightness 574 $r = max(0, min(255, $r + $steps)); 575 $g = max(0, min(255, $g + $steps)); 576 $b = max(0, min(255, $b + $steps)); 577 578 // Convert back to hex 579 return '#' . str_pad(dechex($r), 2, '0', STR_PAD_LEFT) 580 . str_pad(dechex($g), 2, '0', STR_PAD_LEFT) 581 . str_pad(dechex($b), 2, '0', STR_PAD_LEFT); 582 } 583 437 584 add_action('wp_head', 'andy_injector_custom_style'); 438 585 function andy_injector_custom_style() { … … 440 587 $primary = get_option('andy_injector_primary_color', '#007bff'); 441 588 589 // Generate color variants 590 $primary_dark = andy_adjust_brightness($primary, -25); 591 $primary_light = andy_adjust_brightness($primary, 140); 592 442 593 // Dynamic CSS injection 443 $custom_css = ":root { --primary-color: " . esc_attr($primary) . " !important; }"; 594 $custom_css = ":root { 595 --primary-color: " . esc_attr($primary) . " !important; 596 --primary-color-dark: " . $primary_dark . " !important; 597 --primary-color-light: " . $primary_light . " !important; 598 --primary-color-shadow: " . $primary . "33 !important; /* 20% opacity */ 599 }"; 444 600 445 601 wp_enqueue_style('andy-style', $plugin_url . 'Andy/assets/Andy-Widget/styles/Andy.css', [], '2.0.7'); … … 478 634 */ 479 635 function andy_injector_open_on_load_render() { 480 $value = get_option('andy_injector_open_on_load', '0'); 481 482 echo '<input type="checkbox" id="andy_injector_open_on_load" name="andy_injector_open_on_load" value="1" ' . checked($value, '1', false) . ' /> '; 636 $options = get_option('andy_injector_open_on_load', '0'); 637 ?> 638 <input type='checkbox' name='andy_injector_open_on_load' value='1' <?php checked('1', $options); ?>> 639 <label for="andy_injector_open_on_load">Cochez pour ouvrir automatiquement la fenêtre de chat au chargement de la page.</label> 640 <?php 641 } 642 643 // Contact section description 644 function andy_contact_section_callback() { 645 echo '<p>' . esc_html__('Ajoutez vos liens de contact pour les intégrer au widget. Tous les champs sont optionnels.', 'andy-votre-assistant-intelligent') . '</p>'; 646 } 647 648 // Render contact field 649 function andy_contact_field_render($args) { 650 $option = get_option($args['label_for']); 651 $type = isset($args['type']) ? $args['type'] : 'text'; 652 $placeholder = isset($args['placeholder']) ? $args['placeholder'] : ''; 653 654 // Special handling for different field types 655 switch ($type) { 656 case 'email': 657 $value = esc_attr($option); 658 break; 659 case 'tel': 660 $value = esc_attr($option); 661 break; 662 case 'text': 663 $value = esc_attr($option); 664 break; 665 default: // url 666 $value = esc_url($option); 667 } 668 ?> 669 <input type="<?php echo esc_attr($type); ?>" 670 id="<?php echo esc_attr($args['label_for']); ?>" 671 name="<?php echo esc_attr($args['label_for']); ?>" 672 value="<?php echo $value; ?>" 673 placeholder="<?php echo esc_attr($placeholder); ?>" 674 class="regular-text"> 675 <?php 483 676 } 484 677 -
andy-votre-assistant-intelligent/tags/2.1.0/Andy/Andy.html
r3291395 r3317904 17 17 <!-- ANDY WIDGET --> 18 18 <div class="andy-position-right"> 19 <!-- Tooltip --> 20 <div id="chat-tooltip" class="medium"> 21 <span id="tooltip-text" class="no-select"></span> 19 <!-- Contact Methods Toggle --> 20 <div id="contact-toggle" class="contact-methods-toggle medium" title="Contact Us"> 21 <?xml version="1.0" encoding="iso-8859-1"?> 22 <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> 23 <svg fill="#ffffff" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 24 viewBox="0 0 32.055 32.055" xml:space="preserve"> 25 <g> 26 <path d="M3.968,12.061C1.775,12.061,0,13.835,0,16.027c0,2.192,1.773,3.967,3.968,3.967c2.189,0,3.966-1.772,3.966-3.967 27 C7.934,13.835,6.157,12.061,3.968,12.061z M16.233,12.061c-2.188,0-3.968,1.773-3.968,3.965c0,2.192,1.778,3.967,3.968,3.967 28 s3.97-1.772,3.97-3.967C20.201,13.835,18.423,12.061,16.233,12.061z M28.09,12.061c-2.192,0-3.969,1.774-3.969,3.967 29 c0,2.19,1.774,3.965,3.969,3.965c2.188,0,3.965-1.772,3.965-3.965S30.278,12.061,28.09,12.061z"/> 30 </g> 31 </svg> 22 32 </div> 23 33 24 <!-- Bulle de chat --> 25 <div id="chat-bubble" class="no-select medium"> 26 <svg 27 fill="#ffffff" 28 29 version="1.1" 30 id="Capa_1" 31 xmlns="http://www.w3.org/2000/svg" 32 xmlns:xlink="http://www.w3.org/1999/xlink" 33 34 viewBox="0 0 60 60" 35 36 xml:space="preserve" 37 > 38 <path d="M30,1.5c-16.542,0-30,12.112-30,27c0,5.204,1.646,10.245,4.768,14.604c-0.591,6.537-2.175,11.39-4.475,13.689 c-0.304,0.304-0.38,0.769-0.188,1.153C0.275,58.289,0.625,58.5,1,58.5c0.046,0,0.092-0.003,0.139-0.01 c0.405-0.057,9.813-1.411,16.618-5.339C21.621,54.71,25.737,55.5,30,55.5c16.542,0,30-12.112,30-27S46.542,1.5,30,1.5z M16,32.5 c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S18.206,32.5,16,32.5z M30,32.5c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4 S32.206,32.5,30,32.5z M44,32.5c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S46.206,32.5,44,32.5z"/> 39 </svg> 34 <!-- Contact Methods List --> 35 <div id="contact-methods" class="contact-methods-list"> 36 <div class="contact-methods-header"> 37 <h3>Nous contacter :</h3> 38 <button class="close-contact-methods"> 39 <svg viewBox="0 0 24 24" fill="currentColor"> 40 <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> 41 </svg> 42 </button> 43 </div> 44 <div class="contact-methods-content"> 45 <!-- Will be populated by JavaScript --> 46 47 </div> 40 48 </div> 41 49 -
andy-votre-assistant-intelligent/tags/2.1.0/Andy/assets/Andy-Widget/scripts/Andy.js
r3309069 r3317904 2 2 const WEB_SOCKET_URL = `wss://dashboard.essayez-andy.fr/ws/`; 3 3 4 // DOM Elements 4 5 const chatTooltip = document.getElementById('chat-tooltip'); 5 6 const chatTooltipSpan = document.getElementById('tooltip-text'); 6 const chatBubble = document.getElementById('chat-bubble');7 7 const chatBox = document.getElementById('chatbox'); 8 8 const chatContent = document.getElementById('chat-content'); 9 9 const chatInput = document.getElementById('chat-input'); 10 10 const contactToggle = document.getElementById('contact-toggle'); 11 const contactMethodsList = document.getElementById('contact-methods'); 12 const contactMethodsContent = document.querySelector('.contact-methods-content'); 13 const closeContactMethods = document.querySelector('.close-contact-methods'); 14 15 // Buttons 11 16 const sendButton = document.getElementById('send-button'); 12 17 const microphoneButton = document.getElementById('microphone-button'); 13 18 const closeButton = document.getElementById('close-button'); 19 20 // Icons for contact methods 21 const CONTACT_ICONS = { 22 'messenger_dm': 'fab fa-facebook-messenger', 23 'tiktok_page': 'fab fa-tiktok', 24 'insta_page': 'fab fa-instagram', 25 'whatsapp_dm': 'fab fa-whatsapp', 26 'facebook_page': 'fab fa-facebook-f', 27 'linkedin_page': 'fab fa-linkedin-in', 28 'mail': 'fas fa-envelope', 29 'tel': 'fas fa-phone', 30 'adresse': 'fas fa-map-marker-alt', 31 'contact_page': 'fas fa-link', 32 'twitter': 'fab fa-twitter', 33 'youtube': 'fab fa-youtube' 34 }; 35 36 // Labels for contact methods 37 const CONTACT_LABELS = { 38 'messenger_dm': 'Messenger', 39 'tiktok_page': 'TikTok', 40 'insta_page': 'Instagram', 41 'whatsapp_dm': 'WhatsApp', 42 'facebook_page': 'Facebook', 43 'linkedin_page': 'LinkedIn', 44 'mail': 'Email', 45 'tel': 'Téléphone', 46 'adresse': 'Adresse', 47 'contact_page': 'Contact', 48 'twitter': 'Twitter', 49 'youtube': 'YouTube' 50 }; 14 51 /* CONST SECTION END */ 52 53 // Initialize contact methods when DOM is loaded 54 document.addEventListener('DOMContentLoaded', function() { 55 // Add Font Awesome if not already added 56 if (!document.querySelector('link[href*="font-awesome"]')) { 57 const fontAwesome = document.createElement('link'); 58 fontAwesome.rel = 'stylesheet'; 59 fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'; 60 document.head.appendChild(fontAwesome); 61 } 62 63 // Initialize contact methods if they exist 64 if (typeof andyContactMethods !== 'undefined') { 65 initContactMethods(); 66 } 67 }); 68 69 // Toggle contact methods list 70 function toggleContactMethods() { 71 contactMethodsList.classList.toggle('active'); 72 } 73 74 // Close contact methods list 75 function closeContactMethodsList() { 76 contactMethodsList.classList.remove('active'); 77 } 78 79 // Initialize contact methods 80 function initContactMethods() { 81 // Clear existing content 82 contactMethodsContent.innerHTML = ''; 83 84 // Always add live chat first 85 const liveChatItem = document.createElement('a'); 86 liveChatItem.className = 'contact-method-item'; 87 liveChatItem.href = '#'; 88 liveChatItem.title = 'Live Chat'; 89 90 /* CHAT BUBBLE CLICK EVENT SECTION */ 91 // Lorsque la bulle de chat est cliquée, on affiche la fenêtre 92 liveChatItem.onclick = function() { 93 chatBox.style.display = (chatBox.style.display === 'none' || chatBox.style.display === '') ? 'flex' : 'none'; 94 95 if (chatBox.style.display === 'flex') { 96 closeContactMethodsList(); 97 } 98 99 chatInput.focus(); 100 101 if ( 102 chatBox.style.display === 'flex' && 103 chatTooltip.classList.contains('visible') 104 ) { 105 chatTooltip.classList.remove('visible'); 106 } 107 108 if ( 109 webSocket === null && 110 111 !doesCookieExist('Andy-Conversation-Uuid') && 112 !doesCookieExist('Andy-Conversation-Human-Requested') 113 ) { 114 initConversation(); 115 } 116 }; 117 /* CHAT BUBBLE CLICK EVENT END */ 118 119 const liveChatIcon = document.createElement('i'); 120 liveChatIcon.className = 'fas fa-comment-dots'; 121 liveChatItem.appendChild(liveChatIcon); 122 contactMethodsContent.appendChild(liveChatItem); 123 124 // Add other contact methods 125 Object.entries(andyContactMethods).forEach(([key, value]) => { 126 if (!value) return; 127 128 const contactItem = document.createElement('a'); 129 contactItem.className = 'contact-method-item'; 130 contactItem.href = getContactUrl(key, value); 131 contactItem.target = key === 'mail' || key === 'tel' ? '_self' : '_blank'; 132 contactItem.rel = 'noopener noreferrer'; 133 contactItem.title = CONTACT_LABELS[key] || key.replace('_', ' '); 134 135 const icon = document.createElement('i'); 136 icon.className = CONTACT_ICONS[key] || 'fas fa-link'; 137 138 contactItem.appendChild(icon); 139 contactMethodsContent.appendChild(contactItem); 140 }); 141 142 // Add event listeners 143 if (contactToggle) { 144 contactToggle.addEventListener('click', toggleContactMethods); 145 } 146 147 if (closeContactMethods) { 148 closeContactMethods.addEventListener('click', closeContactMethodsList); 149 } 150 151 // Close when clicking outside 152 document.addEventListener('click', (e) => { 153 if (!contactMethodsList.contains(e.target) && 154 !contactToggle.contains(e.target) && 155 contactMethodsList.classList.contains('active')) { 156 closeContactMethodsList(); 157 } 158 }); 159 } 160 161 // Get proper URL for contact method 162 function getContactUrl(type, value) { 163 switch (type) { 164 case 'mail': 165 return `mailto:${value}`; 166 case 'tel': 167 return `tel:${value.replace(/\s+/g, '')}`; 168 case 'whatsapp_dm': 169 return `https://wa.me/${value.replace(/[^0-9]/g, '')}`; 170 default: 171 return value; 172 } 173 } 15 174 16 175 /* VARIABLES SECTION */ … … 747 906 748 907 749 /* CHAT BUBBLE CLICK EVENT SECTION */750 // Lorsque la bulle de chat est cliquée, on affiche la fenêtre751 chatBubble.onclick = function() {752 chatBox.style.display = (chatBox.style.display === 'none' || chatBox.style.display === '') ? 'flex' : 'none';753 754 chatInput.focus();755 756 if (757 chatBox.style.display === 'flex' &&758 chatTooltip.classList.contains('visible')759 ) {760 chatTooltip.classList.remove('visible');761 }762 763 if (764 webSocket === null &&765 766 !doesCookieExist('Andy-Conversation-Uuid') &&767 !doesCookieExist('Andy-Conversation-Human-Requested')768 ) {769 initConversation();770 }771 };772 /* CHAT BUBBLE CLICK EVENT END */773 908 774 909 /* SEND MESSAGE ON ENTER SECTION */ … … 935 1070 /* MARKDOWN TO HTML SECTION END */ 936 1071 937 /* CODE SECTION FOR INACTIVITY AND POST-ACTIVITY DELAY */938 (async () => {939 try {940 const response = await fetch('https://dashboard.essayez-andy.fr/api/tooltip');941 const data = await response.json();942 943 chatTooltipSpan.textContent = data.tooltip;944 } catch (err) {945 console.error(err);946 947 return;948 }949 950 if (chatBox.style.display !== 'flex') {951 chatTooltip.classList.add('visible');952 }953 954 let inactivityDelay = 30000; // 30 secondes d'inactivité955 let postActivityDelay = 3000; // 3 secondes après retour d'activité956 957 let inactivityTimeout;958 let postActivityTimeout;959 960 let userIsInactive = false;961 962 function handleInactivity() {963 userIsInactive = true;964 965 if (chatBox.style.display === 'flex') {966 return;967 }968 969 if (!chatTooltip.classList.contains('visible')) {970 chatTooltip.classList.add('visible');971 }972 }973 974 function handlePostReactivity() {975 if (chatTooltip.classList.contains('visible')) {976 chatTooltip.classList.remove('visible');977 }978 }979 980 function onUserActivity() {981 // Si utilisateur était inactif, on déclenche un timer pour l'action post-réactivité982 if (userIsInactive) {983 clearTimeout(postActivityTimeout);984 postActivityTimeout = setTimeout(handlePostReactivity, postActivityDelay);985 }986 987 userIsInactive = false;988 clearTimeout(inactivityTimeout);989 inactivityTimeout = setTimeout(handleInactivity, inactivityDelay);990 }991 992 // Événements écoutés pour détecter l'activité993 ["mousemove", "keydown", "scroll", "click"].forEach(event =>994 document.addEventListener(event, onUserActivity)995 );996 997 // Démarre le premier timer998 inactivityTimeout = setTimeout(handleInactivity, inactivityDelay);999 1000 setTimeout(() => {1001 if (chatTooltip.classList.contains('visible')) {1002 chatTooltip.classList.remove('visible');1003 }1004 }, 5000);1005 1006 chatBubble.addEventListener('mouseenter', () => {1007 if (chatBox.style.display === 'flex') {1008 return ;1009 }1010 1011 if (!chatTooltip.classList.contains('visible')) {1012 chatTooltip.classList.add('visible');1013 }1014 });1015 1016 chatBubble.addEventListener('mouseleave', () => {1017 if (chatBox.style.display === 'flex') {1018 return ;1019 }1020 1021 if (chatTooltip.classList.contains('visible')) {1022 chatTooltip.classList.remove('visible');1023 }1024 })1025 1026 /*1027 1028 Tooltip shows:1029 -During 5s after page load then disappear1030 -After 30s inactivity then disappear after 3s of post-activity1031 -When hovering the bubble1032 1033 */1034 })();1035 /* #### TOOLTIP SECTION END #### */1036 1037 1072 /* #### REQUEST HUMAN OVERLAY SECTION #### */ 1038 1073 const openHumanRequestOverlayButton = document.getElementById("human-request-header-button"); -
andy-votre-assistant-intelligent/tags/2.1.0/Andy/assets/Andy-Widget/styles/Andy.css
r3297562 r3317904 16 16 17 17 strong { font-weight: bold; } 18 19 /* Contact Methods Styles */ 20 .contact-methods-toggle { 21 position: relative; 22 display: flex; 23 align-items: center; 24 justify-content: center; 25 width: 40px; 26 height: 40px; 27 background: var(--primary-color, #4a6cf7); 28 border: none; 29 border-radius: 50%; 30 cursor: pointer; 31 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); 32 transition: all 0.3s ease; 33 padding: 0; 34 } 35 36 .contact-methods-toggle:hover { 37 background: var(--primary-color-dark, #3a5ce4); 38 box-shadow: 0 4px 12px rgba(74, 108, 247, 0.2); 39 } 40 41 .contact-methods-toggle svg { 42 width: 24px; 43 height: 24px; 44 color: white; 45 transition: all 0.2s ease; 46 } 47 48 .contact-methods-toggle:hover svg { 49 transform: scale(1.1); 50 opacity: 0.9; 51 } 52 53 .contact-methods-list { 54 position: fixed; 55 right: 24px; 56 bottom: 80px; 57 width: 300px; 58 max-height: 70vh; 59 background: white; 60 border-radius: 12px; 61 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); 62 overflow: hidden; 63 transform: translateY(20px); 64 opacity: 0; 65 visibility: hidden; 66 transition: all 0.3s ease; 67 z-index: 999999; 68 } 69 70 .contact-methods-list.active { 71 transform: translateY(0); 72 opacity: 1; 73 visibility: visible; 74 } 75 76 .contact-methods-header { 77 display: flex; 78 justify-content: space-between; 79 align-items: center; 80 padding: 16px 20px; 81 background: #f8f9fa; 82 border-bottom: 1px solid #e9ecef; 83 } 84 85 .contact-methods-header h3 { 86 margin: 0; 87 font-size: 16px; 88 color: #2d3748; 89 } 90 91 .close-contact-methods { 92 background: none; 93 border: none; 94 color: #6c757d; 95 cursor: pointer; 96 padding: 4px; 97 border-radius: 4px; 98 transition: all 0.2s ease; 99 } 100 101 .close-contact-methods:hover { 102 background: #e9ecef; 103 color: #2d3748; 104 } 105 106 .close-contact-methods svg { 107 width: 20px; 108 height: 20px; 109 } 110 111 .contact-methods-content { 112 max-height: calc(70vh - 60px); 113 overflow-y: auto; 114 /* padding: 16px 0; */ 115 } 116 117 .contact-method-item { 118 display: flex; 119 flex-direction: column; 120 align-items: center; 121 justify-content: center; 122 width: 50px; 123 height: 50px; 124 margin: 10px; 125 background: white; 126 border-radius: 50%; 127 color: var(--primary-color, #4a6cf7); 128 text-decoration: none; 129 transition: all 0.2s ease; 130 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 131 border: 2px solid var(--primary-color, #4a6cf7); 132 } 133 134 .contact-method-item:hover { 135 background: var(--primary-color-light, #f0f4ff); 136 transform: translateY(-2px); 137 box-shadow: 0 4px 12px var(--primary-color-shadow, rgba(74, 108, 247, 0.2)); 138 } 139 140 .contact-method-item i { 141 font-size: 24px; 142 transition: all 0.2s ease; 143 color: var(--primary-color, #4a6cf7); 144 } 145 146 .contact-method-item:hover i { 147 transform: scale(1.1); 148 color: var(--primary-color-dark, #3a5ce4); 149 } 150 151 .contact-methods-content { 152 display: flex; 153 flex-wrap: wrap; 154 justify-content: center; 155 /* padding: 10px 10px; */ 156 } 157 158 /* Position classes */ 159 .andy-position-right { 160 position: fixed; 161 right: 24px; 162 bottom: 24px; 163 z-index: 999998; 164 } 165 166 .andy-position-left { 167 position: fixed; 168 left: 24px; 169 bottom: 24px; 170 z-index: 999998; 171 } 172 173 .andy-position-left #chatbox { 174 left: 24px; 175 bottom: 24px; 176 } 177 178 .andy-position-right #chatbox { 179 right: 24px; 180 bottom: 24px; 181 } 182 183 /* Contact list positioning based on button size */ 184 .contact-methods-list { 185 bottom: 100px; /* Default for medium */ 186 } 187 188 /* Small button */ 189 .contact-methods-toggle.small + .contact-methods-list { 190 bottom: 85px; 191 } 192 193 /* Medium button (default) */ 194 .contact-methods-toggle.medium + .contact-methods-list { 195 bottom: 100px; 196 } 197 198 /* Big button */ 199 .contact-methods-toggle.big + .contact-methods-list { 200 bottom: 110px; 201 } 202 203 /* Very big button */ 204 .contact-methods-toggle.very-big + .contact-methods-list { 205 bottom: 120px; 206 } 207 208 /* Left position adjustments */ 209 .andy-position-left .contact-methods-list { 210 right: auto; 211 left: 24px; 212 } 213 214 /* Responsive */ 215 @media (max-width: 480px) { 216 .contact-methods-list { 217 width: calc(100% - 32px); 218 right: 16px; 219 bottom: 72px; 220 } 221 222 .andy-position-left .contact-methods-list { 223 left: 16px; 224 } 225 } 18 226 19 227 menu, ol, ul, li { … … 24 232 } 25 233 26 .andy-position-right #chat-bubble,27 .andy-position-right #chatbox {28 right: 20px;29 left: auto;30 }31 32 .andy-position-left #chat-bubble,33 .andy-position-left #chatbox {34 left: 20px;35 right: auto;36 }37 38 234 .andy-position-right #chat-tooltip.small { 39 235 bottom: 70px; … … 117 313 } 118 314 119 #c hat-bubble.small {315 #contact-toggle.small { 120 316 width: 50px; 121 317 height: 50px; 122 318 } 123 319 124 #c hat-bubble.small svg {320 #contact-toggle.small svg { 125 321 width: 25px; 126 322 height: 25px; 127 323 } 128 324 129 #c hat-bubble.medium {325 #contact-toggle.medium { 130 326 width: 60px; 131 327 height: 60px; 132 328 } 133 329 134 #c hat-bubble.medium svg {330 #contact-toggle.medium svg { 135 331 width: 30px; 136 332 height: 30px; 137 333 } 138 334 139 #c hat-bubble.big {335 #contact-toggle.big { 140 336 width: 70px; 141 337 height: 70px; 142 338 } 143 339 144 #c hat-bubble.big svg {340 #contact-toggle.big svg { 145 341 width: 35px; 146 342 height: 35px; 147 343 } 148 344 149 #c hat-bubble.very-big {345 #contact-toggle.very-big { 150 346 width: 80px; 151 347 height: 80px; 152 348 } 153 349 154 #c hat-bubble.very-big svg {350 #contact-toggle.very-big svg { 155 351 width: 40px; 156 352 height: 40px; … … 185 381 overscroll-behavior-y: contain; 186 382 } 187 188 #chat-bubble.small + #chatbox { bottom: 85px; }189 #chat-bubble.medium + #chatbox { bottom: 95px; }190 #chat-bubble.big + #chatbox { bottom: 105px; }191 #chat-bubble.very-big + #chatbox { bottom: 115px; }192 383 193 384 #chat-content { -
andy-votre-assistant-intelligent/tags/2.1.0/readme.txt
r3309069 r3317904 6 6 Requires PHP: 7.3 7 7 Donate link: https://essayez-andy.fr 8 Stable tag: 2. 0.78 Stable tag: 2.1.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html -
andy-votre-assistant-intelligent/trunk/Andy-Wordress-Extension.php
r3309069 r3317904 91 91 $plugin_url = plugin_dir_url(__FILE__); 92 92 93 // Get all contact methods 94 $contact_methods = []; 95 $contact_fields = [ 96 'messenger_dm', 'tiktok_page', 'insta_page', 'whatsapp_dm', 97 'facebook_page', 'linkedin_page', 'mail', 'tel', 'adresse', 98 'contact_page', 'twitter', 'youtube' 99 ]; 100 101 foreach ($contact_fields as $field) { 102 $value = get_option('andy_contact_' . $field, ''); 103 if (!empty($value)) { 104 $contact_methods[$field] = $value; 105 } 106 } 107 93 108 // Check WP version 94 109 global $wp_version; … … 99 114 'strategy' => 'defer', 100 115 ]); 116 117 // Pass contact methods to JavaScript 118 wp_localize_script('andy-script', 'andyContactMethods', $contact_methods); 101 119 } else { 102 120 // Old WordPress way … … 414 432 'andy_injector_section' 415 433 ); 434 435 // Add Moyens de contact section 436 add_settings_section( 437 'andy_contact_section', 438 esc_html__('Moyens de contact', 'andy-votre-assistant-intelligent'), 439 'andy_contact_section_callback', 440 'andyInjector' 441 ); 442 443 // Define contact methods with their specific configurations 444 $contact_methods = [ 445 'messenger_dm' => [ 446 'label' => 'Lien Messenger DM', 447 'type' => 'url', 448 'placeholder' => 'https://m.me/votrenom', 449 'sanitize' => 'esc_url_raw' 450 ], 451 'tiktok_page' => [ 452 'label' => 'Page TikTok', 453 'type' => 'url', 454 'placeholder' => 'https://www.tiktok.com/@votrenom', 455 'sanitize' => 'esc_url_raw' 456 ], 457 'insta_page' => [ 458 'label' => 'Page Instagram', 459 'type' => 'url', 460 'placeholder' => 'https://www.instagram.com/votrenom', 461 'sanitize' => 'esc_url_raw' 462 ], 463 'whatsapp_dm' => [ 464 'label' => 'Lien WhatsApp DM', 465 'type' => 'url', 466 'placeholder' => 'https://wa.me/33612345678', 467 'sanitize' => 'esc_url_raw' 468 ], 469 'facebook_page' => [ 470 'label' => 'Page Facebook', 471 'type' => 'url', 472 'placeholder' => 'https://www.facebook.com/votrepage', 473 'sanitize' => 'esc_url_raw' 474 ], 475 'linkedin_page' => [ 476 'label' => 'Page LinkedIn', 477 'type' => 'url', 478 'placeholder' => 'https://www.linkedin.com/company/votresociete', 479 'sanitize' => 'esc_url_raw' 480 ], 481 'mail' => [ 482 'label' => 'Adresse email', 483 'type' => 'email', 484 'placeholder' => '[email protected]', 485 'sanitize' => 'sanitize_email' 486 ], 487 'tel' => [ 488 'label' => 'Numéro de téléphone', 489 'type' => 'tel', 490 'placeholder' => '+33 6 12 34 56 78', 491 'sanitize' => 'sanitize_text_field' 492 ], 493 'adresse' => [ 494 'label' => 'Adresse physique', 495 'type' => 'text', 496 'placeholder' => '123 Rue Exemple, 75000 Paris', 497 'sanitize' => 'sanitize_text_field' 498 ], 499 'contact_page' => [ 500 'label' => 'Lien page de contact', 501 'type' => 'url', 502 'placeholder' => 'https://votresite.com/contact', 503 'sanitize' => 'esc_url_raw' 504 ], 505 'twitter' => [ 506 'label' => 'Page Twitter (X)', 507 'type' => 'url', 508 'placeholder' => 'https://twitter.com/votrenom', 509 'sanitize' => 'esc_url_raw' 510 ], 511 'youtube' => [ 512 'label' => 'Chaîne YouTube', 513 'type' => 'url', 514 'placeholder' => 'https://www.youtube.com/@votrechaine', 515 'sanitize' => 'esc_url_raw' 516 ] 517 ]; 518 519 // Register and add settings fields for each contact method 520 foreach ($contact_methods as $key => $config) { 521 $option_name = 'andy_contact_' . $key; 522 523 register_setting('andyInjector', $option_name, [ 524 'sanitize_callback' => $config['sanitize'], 525 ]); 526 527 add_settings_field( 528 $option_name, 529 esc_html__($config['label'], 'andy-votre-assistant-intelligent'), 530 'andy_contact_field_render', 531 'andyInjector', 532 'andy_contact_section', 533 [ 534 'label_for' => $option_name, 535 'placeholder' => $config['placeholder'], 536 'type' => $config['type'] 537 ] 538 ); 539 } 416 540 } 417 541 … … 435 559 } 436 560 561 /** 562 * Adjust color brightness 563 */ 564 function andy_adjust_brightness($hex, $steps) { 565 // Remove # from hex code if present 566 $hex = ltrim($hex, '#'); 567 568 // Convert to RGB 569 $r = hexdec(substr($hex, 0, 2)); 570 $g = hexdec(substr($hex, 2, 2)); 571 $b = hexdec(substr($hex, 4, 2)); 572 573 // Adjust brightness 574 $r = max(0, min(255, $r + $steps)); 575 $g = max(0, min(255, $g + $steps)); 576 $b = max(0, min(255, $b + $steps)); 577 578 // Convert back to hex 579 return '#' . str_pad(dechex($r), 2, '0', STR_PAD_LEFT) 580 . str_pad(dechex($g), 2, '0', STR_PAD_LEFT) 581 . str_pad(dechex($b), 2, '0', STR_PAD_LEFT); 582 } 583 437 584 add_action('wp_head', 'andy_injector_custom_style'); 438 585 function andy_injector_custom_style() { … … 440 587 $primary = get_option('andy_injector_primary_color', '#007bff'); 441 588 589 // Generate color variants 590 $primary_dark = andy_adjust_brightness($primary, -25); 591 $primary_light = andy_adjust_brightness($primary, 140); 592 442 593 // Dynamic CSS injection 443 $custom_css = ":root { --primary-color: " . esc_attr($primary) . " !important; }"; 594 $custom_css = ":root { 595 --primary-color: " . esc_attr($primary) . " !important; 596 --primary-color-dark: " . $primary_dark . " !important; 597 --primary-color-light: " . $primary_light . " !important; 598 --primary-color-shadow: " . $primary . "33 !important; /* 20% opacity */ 599 }"; 444 600 445 601 wp_enqueue_style('andy-style', $plugin_url . 'Andy/assets/Andy-Widget/styles/Andy.css', [], '2.0.7'); … … 478 634 */ 479 635 function andy_injector_open_on_load_render() { 480 $value = get_option('andy_injector_open_on_load', '0'); 481 482 echo '<input type="checkbox" id="andy_injector_open_on_load" name="andy_injector_open_on_load" value="1" ' . checked($value, '1', false) . ' /> '; 636 $options = get_option('andy_injector_open_on_load', '0'); 637 ?> 638 <input type='checkbox' name='andy_injector_open_on_load' value='1' <?php checked('1', $options); ?>> 639 <label for="andy_injector_open_on_load">Cochez pour ouvrir automatiquement la fenêtre de chat au chargement de la page.</label> 640 <?php 641 } 642 643 // Contact section description 644 function andy_contact_section_callback() { 645 echo '<p>' . esc_html__('Ajoutez vos liens de contact pour les intégrer au widget. Tous les champs sont optionnels.', 'andy-votre-assistant-intelligent') . '</p>'; 646 } 647 648 // Render contact field 649 function andy_contact_field_render($args) { 650 $option = get_option($args['label_for']); 651 $type = isset($args['type']) ? $args['type'] : 'text'; 652 $placeholder = isset($args['placeholder']) ? $args['placeholder'] : ''; 653 654 // Special handling for different field types 655 switch ($type) { 656 case 'email': 657 $value = esc_attr($option); 658 break; 659 case 'tel': 660 $value = esc_attr($option); 661 break; 662 case 'text': 663 $value = esc_attr($option); 664 break; 665 default: // url 666 $value = esc_url($option); 667 } 668 ?> 669 <input type="<?php echo esc_attr($type); ?>" 670 id="<?php echo esc_attr($args['label_for']); ?>" 671 name="<?php echo esc_attr($args['label_for']); ?>" 672 value="<?php echo $value; ?>" 673 placeholder="<?php echo esc_attr($placeholder); ?>" 674 class="regular-text"> 675 <?php 483 676 } 484 677 -
andy-votre-assistant-intelligent/trunk/Andy/Andy.html
r3291395 r3317904 17 17 <!-- ANDY WIDGET --> 18 18 <div class="andy-position-right"> 19 <!-- Tooltip --> 20 <div id="chat-tooltip" class="medium"> 21 <span id="tooltip-text" class="no-select"></span> 19 <!-- Contact Methods Toggle --> 20 <div id="contact-toggle" class="contact-methods-toggle medium" title="Contact Us"> 21 <?xml version="1.0" encoding="iso-8859-1"?> 22 <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> 23 <svg fill="#ffffff" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 24 viewBox="0 0 32.055 32.055" xml:space="preserve"> 25 <g> 26 <path d="M3.968,12.061C1.775,12.061,0,13.835,0,16.027c0,2.192,1.773,3.967,3.968,3.967c2.189,0,3.966-1.772,3.966-3.967 27 C7.934,13.835,6.157,12.061,3.968,12.061z M16.233,12.061c-2.188,0-3.968,1.773-3.968,3.965c0,2.192,1.778,3.967,3.968,3.967 28 s3.97-1.772,3.97-3.967C20.201,13.835,18.423,12.061,16.233,12.061z M28.09,12.061c-2.192,0-3.969,1.774-3.969,3.967 29 c0,2.19,1.774,3.965,3.969,3.965c2.188,0,3.965-1.772,3.965-3.965S30.278,12.061,28.09,12.061z"/> 30 </g> 31 </svg> 22 32 </div> 23 33 24 <!-- Bulle de chat --> 25 <div id="chat-bubble" class="no-select medium"> 26 <svg 27 fill="#ffffff" 28 29 version="1.1" 30 id="Capa_1" 31 xmlns="http://www.w3.org/2000/svg" 32 xmlns:xlink="http://www.w3.org/1999/xlink" 33 34 viewBox="0 0 60 60" 35 36 xml:space="preserve" 37 > 38 <path d="M30,1.5c-16.542,0-30,12.112-30,27c0,5.204,1.646,10.245,4.768,14.604c-0.591,6.537-2.175,11.39-4.475,13.689 c-0.304,0.304-0.38,0.769-0.188,1.153C0.275,58.289,0.625,58.5,1,58.5c0.046,0,0.092-0.003,0.139-0.01 c0.405-0.057,9.813-1.411,16.618-5.339C21.621,54.71,25.737,55.5,30,55.5c16.542,0,30-12.112,30-27S46.542,1.5,30,1.5z M16,32.5 c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S18.206,32.5,16,32.5z M30,32.5c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4 S32.206,32.5,30,32.5z M44,32.5c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S46.206,32.5,44,32.5z"/> 39 </svg> 34 <!-- Contact Methods List --> 35 <div id="contact-methods" class="contact-methods-list"> 36 <div class="contact-methods-header"> 37 <h3>Nous contacter :</h3> 38 <button class="close-contact-methods"> 39 <svg viewBox="0 0 24 24" fill="currentColor"> 40 <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> 41 </svg> 42 </button> 43 </div> 44 <div class="contact-methods-content"> 45 <!-- Will be populated by JavaScript --> 46 47 </div> 40 48 </div> 41 49 -
andy-votre-assistant-intelligent/trunk/Andy/assets/Andy-Widget/scripts/Andy.js
r3309069 r3317904 2 2 const WEB_SOCKET_URL = `wss://dashboard.essayez-andy.fr/ws/`; 3 3 4 // DOM Elements 4 5 const chatTooltip = document.getElementById('chat-tooltip'); 5 6 const chatTooltipSpan = document.getElementById('tooltip-text'); 6 const chatBubble = document.getElementById('chat-bubble');7 7 const chatBox = document.getElementById('chatbox'); 8 8 const chatContent = document.getElementById('chat-content'); 9 9 const chatInput = document.getElementById('chat-input'); 10 10 const contactToggle = document.getElementById('contact-toggle'); 11 const contactMethodsList = document.getElementById('contact-methods'); 12 const contactMethodsContent = document.querySelector('.contact-methods-content'); 13 const closeContactMethods = document.querySelector('.close-contact-methods'); 14 15 // Buttons 11 16 const sendButton = document.getElementById('send-button'); 12 17 const microphoneButton = document.getElementById('microphone-button'); 13 18 const closeButton = document.getElementById('close-button'); 19 20 // Icons for contact methods 21 const CONTACT_ICONS = { 22 'messenger_dm': 'fab fa-facebook-messenger', 23 'tiktok_page': 'fab fa-tiktok', 24 'insta_page': 'fab fa-instagram', 25 'whatsapp_dm': 'fab fa-whatsapp', 26 'facebook_page': 'fab fa-facebook-f', 27 'linkedin_page': 'fab fa-linkedin-in', 28 'mail': 'fas fa-envelope', 29 'tel': 'fas fa-phone', 30 'adresse': 'fas fa-map-marker-alt', 31 'contact_page': 'fas fa-link', 32 'twitter': 'fab fa-twitter', 33 'youtube': 'fab fa-youtube' 34 }; 35 36 // Labels for contact methods 37 const CONTACT_LABELS = { 38 'messenger_dm': 'Messenger', 39 'tiktok_page': 'TikTok', 40 'insta_page': 'Instagram', 41 'whatsapp_dm': 'WhatsApp', 42 'facebook_page': 'Facebook', 43 'linkedin_page': 'LinkedIn', 44 'mail': 'Email', 45 'tel': 'Téléphone', 46 'adresse': 'Adresse', 47 'contact_page': 'Contact', 48 'twitter': 'Twitter', 49 'youtube': 'YouTube' 50 }; 14 51 /* CONST SECTION END */ 52 53 // Initialize contact methods when DOM is loaded 54 document.addEventListener('DOMContentLoaded', function() { 55 // Add Font Awesome if not already added 56 if (!document.querySelector('link[href*="font-awesome"]')) { 57 const fontAwesome = document.createElement('link'); 58 fontAwesome.rel = 'stylesheet'; 59 fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'; 60 document.head.appendChild(fontAwesome); 61 } 62 63 // Initialize contact methods if they exist 64 if (typeof andyContactMethods !== 'undefined') { 65 initContactMethods(); 66 } 67 }); 68 69 // Toggle contact methods list 70 function toggleContactMethods() { 71 contactMethodsList.classList.toggle('active'); 72 } 73 74 // Close contact methods list 75 function closeContactMethodsList() { 76 contactMethodsList.classList.remove('active'); 77 } 78 79 // Initialize contact methods 80 function initContactMethods() { 81 // Clear existing content 82 contactMethodsContent.innerHTML = ''; 83 84 // Always add live chat first 85 const liveChatItem = document.createElement('a'); 86 liveChatItem.className = 'contact-method-item'; 87 liveChatItem.href = '#'; 88 liveChatItem.title = 'Live Chat'; 89 90 /* CHAT BUBBLE CLICK EVENT SECTION */ 91 // Lorsque la bulle de chat est cliquée, on affiche la fenêtre 92 liveChatItem.onclick = function() { 93 chatBox.style.display = (chatBox.style.display === 'none' || chatBox.style.display === '') ? 'flex' : 'none'; 94 95 if (chatBox.style.display === 'flex') { 96 closeContactMethodsList(); 97 } 98 99 chatInput.focus(); 100 101 if ( 102 chatBox.style.display === 'flex' && 103 chatTooltip.classList.contains('visible') 104 ) { 105 chatTooltip.classList.remove('visible'); 106 } 107 108 if ( 109 webSocket === null && 110 111 !doesCookieExist('Andy-Conversation-Uuid') && 112 !doesCookieExist('Andy-Conversation-Human-Requested') 113 ) { 114 initConversation(); 115 } 116 }; 117 /* CHAT BUBBLE CLICK EVENT END */ 118 119 const liveChatIcon = document.createElement('i'); 120 liveChatIcon.className = 'fas fa-comment-dots'; 121 liveChatItem.appendChild(liveChatIcon); 122 contactMethodsContent.appendChild(liveChatItem); 123 124 // Add other contact methods 125 Object.entries(andyContactMethods).forEach(([key, value]) => { 126 if (!value) return; 127 128 const contactItem = document.createElement('a'); 129 contactItem.className = 'contact-method-item'; 130 contactItem.href = getContactUrl(key, value); 131 contactItem.target = key === 'mail' || key === 'tel' ? '_self' : '_blank'; 132 contactItem.rel = 'noopener noreferrer'; 133 contactItem.title = CONTACT_LABELS[key] || key.replace('_', ' '); 134 135 const icon = document.createElement('i'); 136 icon.className = CONTACT_ICONS[key] || 'fas fa-link'; 137 138 contactItem.appendChild(icon); 139 contactMethodsContent.appendChild(contactItem); 140 }); 141 142 // Add event listeners 143 if (contactToggle) { 144 contactToggle.addEventListener('click', toggleContactMethods); 145 } 146 147 if (closeContactMethods) { 148 closeContactMethods.addEventListener('click', closeContactMethodsList); 149 } 150 151 // Close when clicking outside 152 document.addEventListener('click', (e) => { 153 if (!contactMethodsList.contains(e.target) && 154 !contactToggle.contains(e.target) && 155 contactMethodsList.classList.contains('active')) { 156 closeContactMethodsList(); 157 } 158 }); 159 } 160 161 // Get proper URL for contact method 162 function getContactUrl(type, value) { 163 switch (type) { 164 case 'mail': 165 return `mailto:${value}`; 166 case 'tel': 167 return `tel:${value.replace(/\s+/g, '')}`; 168 case 'whatsapp_dm': 169 return `https://wa.me/${value.replace(/[^0-9]/g, '')}`; 170 default: 171 return value; 172 } 173 } 15 174 16 175 /* VARIABLES SECTION */ … … 747 906 748 907 749 /* CHAT BUBBLE CLICK EVENT SECTION */750 // Lorsque la bulle de chat est cliquée, on affiche la fenêtre751 chatBubble.onclick = function() {752 chatBox.style.display = (chatBox.style.display === 'none' || chatBox.style.display === '') ? 'flex' : 'none';753 754 chatInput.focus();755 756 if (757 chatBox.style.display === 'flex' &&758 chatTooltip.classList.contains('visible')759 ) {760 chatTooltip.classList.remove('visible');761 }762 763 if (764 webSocket === null &&765 766 !doesCookieExist('Andy-Conversation-Uuid') &&767 !doesCookieExist('Andy-Conversation-Human-Requested')768 ) {769 initConversation();770 }771 };772 /* CHAT BUBBLE CLICK EVENT END */773 908 774 909 /* SEND MESSAGE ON ENTER SECTION */ … … 935 1070 /* MARKDOWN TO HTML SECTION END */ 936 1071 937 /* CODE SECTION FOR INACTIVITY AND POST-ACTIVITY DELAY */938 (async () => {939 try {940 const response = await fetch('https://dashboard.essayez-andy.fr/api/tooltip');941 const data = await response.json();942 943 chatTooltipSpan.textContent = data.tooltip;944 } catch (err) {945 console.error(err);946 947 return;948 }949 950 if (chatBox.style.display !== 'flex') {951 chatTooltip.classList.add('visible');952 }953 954 let inactivityDelay = 30000; // 30 secondes d'inactivité955 let postActivityDelay = 3000; // 3 secondes après retour d'activité956 957 let inactivityTimeout;958 let postActivityTimeout;959 960 let userIsInactive = false;961 962 function handleInactivity() {963 userIsInactive = true;964 965 if (chatBox.style.display === 'flex') {966 return;967 }968 969 if (!chatTooltip.classList.contains('visible')) {970 chatTooltip.classList.add('visible');971 }972 }973 974 function handlePostReactivity() {975 if (chatTooltip.classList.contains('visible')) {976 chatTooltip.classList.remove('visible');977 }978 }979 980 function onUserActivity() {981 // Si utilisateur était inactif, on déclenche un timer pour l'action post-réactivité982 if (userIsInactive) {983 clearTimeout(postActivityTimeout);984 postActivityTimeout = setTimeout(handlePostReactivity, postActivityDelay);985 }986 987 userIsInactive = false;988 clearTimeout(inactivityTimeout);989 inactivityTimeout = setTimeout(handleInactivity, inactivityDelay);990 }991 992 // Événements écoutés pour détecter l'activité993 ["mousemove", "keydown", "scroll", "click"].forEach(event =>994 document.addEventListener(event, onUserActivity)995 );996 997 // Démarre le premier timer998 inactivityTimeout = setTimeout(handleInactivity, inactivityDelay);999 1000 setTimeout(() => {1001 if (chatTooltip.classList.contains('visible')) {1002 chatTooltip.classList.remove('visible');1003 }1004 }, 5000);1005 1006 chatBubble.addEventListener('mouseenter', () => {1007 if (chatBox.style.display === 'flex') {1008 return ;1009 }1010 1011 if (!chatTooltip.classList.contains('visible')) {1012 chatTooltip.classList.add('visible');1013 }1014 });1015 1016 chatBubble.addEventListener('mouseleave', () => {1017 if (chatBox.style.display === 'flex') {1018 return ;1019 }1020 1021 if (chatTooltip.classList.contains('visible')) {1022 chatTooltip.classList.remove('visible');1023 }1024 })1025 1026 /*1027 1028 Tooltip shows:1029 -During 5s after page load then disappear1030 -After 30s inactivity then disappear after 3s of post-activity1031 -When hovering the bubble1032 1033 */1034 })();1035 /* #### TOOLTIP SECTION END #### */1036 1037 1072 /* #### REQUEST HUMAN OVERLAY SECTION #### */ 1038 1073 const openHumanRequestOverlayButton = document.getElementById("human-request-header-button"); -
andy-votre-assistant-intelligent/trunk/Andy/assets/Andy-Widget/styles/Andy.css
r3297562 r3317904 16 16 17 17 strong { font-weight: bold; } 18 19 /* Contact Methods Styles */ 20 .contact-methods-toggle { 21 position: relative; 22 display: flex; 23 align-items: center; 24 justify-content: center; 25 width: 40px; 26 height: 40px; 27 background: var(--primary-color, #4a6cf7); 28 border: none; 29 border-radius: 50%; 30 cursor: pointer; 31 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); 32 transition: all 0.3s ease; 33 padding: 0; 34 } 35 36 .contact-methods-toggle:hover { 37 background: var(--primary-color-dark, #3a5ce4); 38 box-shadow: 0 4px 12px rgba(74, 108, 247, 0.2); 39 } 40 41 .contact-methods-toggle svg { 42 width: 24px; 43 height: 24px; 44 color: white; 45 transition: all 0.2s ease; 46 } 47 48 .contact-methods-toggle:hover svg { 49 transform: scale(1.1); 50 opacity: 0.9; 51 } 52 53 .contact-methods-list { 54 position: fixed; 55 right: 24px; 56 bottom: 80px; 57 width: 300px; 58 max-height: 70vh; 59 background: white; 60 border-radius: 12px; 61 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); 62 overflow: hidden; 63 transform: translateY(20px); 64 opacity: 0; 65 visibility: hidden; 66 transition: all 0.3s ease; 67 z-index: 999999; 68 } 69 70 .contact-methods-list.active { 71 transform: translateY(0); 72 opacity: 1; 73 visibility: visible; 74 } 75 76 .contact-methods-header { 77 display: flex; 78 justify-content: space-between; 79 align-items: center; 80 padding: 16px 20px; 81 background: #f8f9fa; 82 border-bottom: 1px solid #e9ecef; 83 } 84 85 .contact-methods-header h3 { 86 margin: 0; 87 font-size: 16px; 88 color: #2d3748; 89 } 90 91 .close-contact-methods { 92 background: none; 93 border: none; 94 color: #6c757d; 95 cursor: pointer; 96 padding: 4px; 97 border-radius: 4px; 98 transition: all 0.2s ease; 99 } 100 101 .close-contact-methods:hover { 102 background: #e9ecef; 103 color: #2d3748; 104 } 105 106 .close-contact-methods svg { 107 width: 20px; 108 height: 20px; 109 } 110 111 .contact-methods-content { 112 max-height: calc(70vh - 60px); 113 overflow-y: auto; 114 /* padding: 16px 0; */ 115 } 116 117 .contact-method-item { 118 display: flex; 119 flex-direction: column; 120 align-items: center; 121 justify-content: center; 122 width: 50px; 123 height: 50px; 124 margin: 10px; 125 background: white; 126 border-radius: 50%; 127 color: var(--primary-color, #4a6cf7); 128 text-decoration: none; 129 transition: all 0.2s ease; 130 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 131 border: 2px solid var(--primary-color, #4a6cf7); 132 } 133 134 .contact-method-item:hover { 135 background: var(--primary-color-light, #f0f4ff); 136 transform: translateY(-2px); 137 box-shadow: 0 4px 12px var(--primary-color-shadow, rgba(74, 108, 247, 0.2)); 138 } 139 140 .contact-method-item i { 141 font-size: 24px; 142 transition: all 0.2s ease; 143 color: var(--primary-color, #4a6cf7); 144 } 145 146 .contact-method-item:hover i { 147 transform: scale(1.1); 148 color: var(--primary-color-dark, #3a5ce4); 149 } 150 151 .contact-methods-content { 152 display: flex; 153 flex-wrap: wrap; 154 justify-content: center; 155 /* padding: 10px 10px; */ 156 } 157 158 /* Position classes */ 159 .andy-position-right { 160 position: fixed; 161 right: 24px; 162 bottom: 24px; 163 z-index: 999998; 164 } 165 166 .andy-position-left { 167 position: fixed; 168 left: 24px; 169 bottom: 24px; 170 z-index: 999998; 171 } 172 173 .andy-position-left #chatbox { 174 left: 24px; 175 bottom: 24px; 176 } 177 178 .andy-position-right #chatbox { 179 right: 24px; 180 bottom: 24px; 181 } 182 183 /* Contact list positioning based on button size */ 184 .contact-methods-list { 185 bottom: 100px; /* Default for medium */ 186 } 187 188 /* Small button */ 189 .contact-methods-toggle.small + .contact-methods-list { 190 bottom: 85px; 191 } 192 193 /* Medium button (default) */ 194 .contact-methods-toggle.medium + .contact-methods-list { 195 bottom: 100px; 196 } 197 198 /* Big button */ 199 .contact-methods-toggle.big + .contact-methods-list { 200 bottom: 110px; 201 } 202 203 /* Very big button */ 204 .contact-methods-toggle.very-big + .contact-methods-list { 205 bottom: 120px; 206 } 207 208 /* Left position adjustments */ 209 .andy-position-left .contact-methods-list { 210 right: auto; 211 left: 24px; 212 } 213 214 /* Responsive */ 215 @media (max-width: 480px) { 216 .contact-methods-list { 217 width: calc(100% - 32px); 218 right: 16px; 219 bottom: 72px; 220 } 221 222 .andy-position-left .contact-methods-list { 223 left: 16px; 224 } 225 } 18 226 19 227 menu, ol, ul, li { … … 24 232 } 25 233 26 .andy-position-right #chat-bubble,27 .andy-position-right #chatbox {28 right: 20px;29 left: auto;30 }31 32 .andy-position-left #chat-bubble,33 .andy-position-left #chatbox {34 left: 20px;35 right: auto;36 }37 38 234 .andy-position-right #chat-tooltip.small { 39 235 bottom: 70px; … … 117 313 } 118 314 119 #c hat-bubble.small {315 #contact-toggle.small { 120 316 width: 50px; 121 317 height: 50px; 122 318 } 123 319 124 #c hat-bubble.small svg {320 #contact-toggle.small svg { 125 321 width: 25px; 126 322 height: 25px; 127 323 } 128 324 129 #c hat-bubble.medium {325 #contact-toggle.medium { 130 326 width: 60px; 131 327 height: 60px; 132 328 } 133 329 134 #c hat-bubble.medium svg {330 #contact-toggle.medium svg { 135 331 width: 30px; 136 332 height: 30px; 137 333 } 138 334 139 #c hat-bubble.big {335 #contact-toggle.big { 140 336 width: 70px; 141 337 height: 70px; 142 338 } 143 339 144 #c hat-bubble.big svg {340 #contact-toggle.big svg { 145 341 width: 35px; 146 342 height: 35px; 147 343 } 148 344 149 #c hat-bubble.very-big {345 #contact-toggle.very-big { 150 346 width: 80px; 151 347 height: 80px; 152 348 } 153 349 154 #c hat-bubble.very-big svg {350 #contact-toggle.very-big svg { 155 351 width: 40px; 156 352 height: 40px; … … 185 381 overscroll-behavior-y: contain; 186 382 } 187 188 #chat-bubble.small + #chatbox { bottom: 85px; }189 #chat-bubble.medium + #chatbox { bottom: 95px; }190 #chat-bubble.big + #chatbox { bottom: 105px; }191 #chat-bubble.very-big + #chatbox { bottom: 115px; }192 383 193 384 #chat-content { -
andy-votre-assistant-intelligent/trunk/readme.txt
r3309069 r3317904 6 6 Requires PHP: 7.3 7 7 Donate link: https://essayez-andy.fr 8 Stable tag: 2. 0.78 Stable tag: 2.1.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html
Note: See TracChangeset
for help on using the changeset viewer.