Changeset 3401134
- Timestamp:
- 11/23/2025 04:57:21 AM (3 months ago)
- Location:
- madnesschat-button/tags/1.0.2
- Files:
-
- 7 edited
-
admin/class-mcnb-admin.php (modified) (4 diffs)
-
admin/class-mcnb-simple-dashboard.php (modified) (3 diffs)
-
assets/js/mcnb-dashboard.js (modified) (4 diffs)
-
includes/class-mcnb-analytics.php (modified) (1 diff)
-
includes/class-mcnb-settings.php (modified) (24 diffs)
-
madnesschat-button.php (modified) (2 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
madnesschat-button/tags/1.0.2/admin/class-mcnb-admin.php
r3398221 r3401134 15 15 // Añadir branding profesional 16 16 add_action( 'admin_notices', array( $this, 'show_professional_branding' ) ); 17 18 // Mostrar notice de review después de guardar 19 add_action( 'admin_notices', array( $this, 'show_review_notice' ) ); 20 21 // Manejar dismiss del notice de review 22 add_action( 'wp_ajax_mcnb_dismiss_review_notice', array( $this, 'dismiss_review_notice' ) ); 17 23 } 18 24 … … 75 81 ?> 76 82 <div class="wrap mcnb-settings"> 77 <h1><?php esc_html_e( 'MadnessChat Button', 'madnesschat-button' ); ?></h1>78 83 <h2 class="nav-tab-wrapper"> 79 84 <a href="<?php echo esc_url( admin_url( 'admin.php?page=mcnb&tab=basic' ) ); ?>" class="nav-tab <?php echo ( 'basic' === $active_tab ) ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Básico', 'madnesschat-button' ); ?></a> … … 237 242 <?php endif; ?> 238 243 </div> 244 245 <!-- Footer con call-to-action para reviews --> 246 <div class="mcnb-review-footer" style="margin-top: 40px; padding: 20px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; text-align: center;"> 247 <p style="margin: 0 0 12px 0; font-size: 15px; color: #495057;"> 248 <span style="font-size: 18px; margin-right: 6px;">⭐</span> 249 <strong><?php esc_html_e( '¿Te gusta MadnessChat Button?', 'madnesschat-button' ); ?></strong> 250 </p> 251 <p style="margin: 0 0 16px 0; font-size: 14px; color: #6c757d;"> 252 <?php esc_html_e( 'Ayudanos con una reseña positiva, ¡toma menos de 10 segundos!', 'madnesschat-button' ); ?> 253 </p> 254 <p style="margin: 0;"> 255 <a href="https://wordpress.org/support/plugin/madnesschat-button/reviews/#new-post" target="_blank" rel="noopener noreferrer" class="button button-primary" style="text-decoration: none;"> 256 <span class="dashicons dashicons-star-filled" style="font-size: 16px; vertical-align: middle; margin-right: 4px;"></span> 257 <?php esc_html_e( 'Dejar una reseña', 'madnesschat-button' ); ?> 258 <span class="dashicons dashicons-external" style="font-size: 14px; vertical-align: middle; margin-left: 4px;"></span> 259 </a> 260 </p> 261 </div> 239 262 </div> 240 263 <?php … … 521 544 return $plugin_data['Version'] ?? '1.0.2'; 522 545 } 546 547 /** 548 * Mostrar notice discreto para pedir reviews después de guardar 549 */ 550 public function show_review_notice() { 551 $screen = get_current_screen(); 552 if ( ! $screen || $screen->id !== 'toplevel_page_mcnb' ) { 553 return; 554 } 555 556 // Verificar si el usuario ya descartó el notice 557 $dismissed = get_user_meta( get_current_user_id(), 'mcnb_review_notice_dismissed', true ); 558 if ( $dismissed ) { 559 return; 560 } 561 562 // Solo mostrar si se guardó recientemente (en los últimos 30 segundos) 563 // o si es la primera vez que se guarda 564 $last_save = get_user_meta( get_current_user_id(), 'mcnb_last_save', true ); 565 $settings_saved = isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] === 'true'; 566 567 if ( ! $settings_saved && ( ! $last_save || ( time() - $last_save ) > 30 ) ) { 568 return; 569 } 570 571 ?> 572 <div class="notice notice-info is-dismissible mcnb-review-notice" style="border-left-color: #25D366; padding: 15px 20px;"> 573 <p style="margin: 0; font-size: 14px; line-height: 1.6;"> 574 <span style="font-size: 18px; margin-right: 8px; vertical-align: middle;">⭐</span> 575 <strong><?php esc_html_e( '¿Te gusta MadnessChat Button?', 'madnesschat-button' ); ?></strong> 576 <?php esc_html_e( 'Ayudanos con una reseña positiva, ¡toma menos de 10 segundos!', 'madnesschat-button' ); ?> 577 <a href="https://wordpress.org/support/plugin/madnesschat-button/reviews/#new-post" target="_blank" rel="noopener noreferrer" class="button button-primary" style="margin-left: 12px; vertical-align: middle;"> 578 <?php esc_html_e( 'Dejar reseña', 'madnesschat-button' ); ?> 579 <span class="dashicons dashicons-external" style="font-size: 14px; vertical-align: middle; margin-left: 4px;"></span> 580 </a> 581 </p> 582 </div> 583 <script> 584 jQuery(document).ready(function($) { 585 $(document).on('click', '.mcnb-review-notice .notice-dismiss', function() { 586 $.ajax({ 587 url: ajaxurl, 588 type: 'POST', 589 data: { 590 action: 'mcnb_dismiss_review_notice', 591 nonce: '<?php echo esc_js( wp_create_nonce( 'mcnb_dismiss_review_notice' ) ); ?>' 592 } 593 }); 594 }); 595 }); 596 </script> 597 <?php 598 } 599 600 /** 601 * Manejar dismiss del notice de review 602 */ 603 public function dismiss_review_notice() { 604 if ( ! current_user_can( 'manage_options' ) ) { 605 wp_die( -1, 403 ); 606 } 607 608 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 609 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; 610 if ( ! wp_verify_nonce( $nonce, 'mcnb_dismiss_review_notice' ) ) { 611 wp_die( -1, 403 ); 612 } 613 614 update_user_meta( get_current_user_id(), 'mcnb_review_notice_dismissed', true ); 615 wp_send_json_success(); 616 } 523 617 } 524 618 -
madnesschat-button/tags/1.0.2/admin/class-mcnb-simple-dashboard.php
r3398221 r3401134 108 108 <div class="mcnb-dashboard-actions"> 109 109 <select id="mcnb-period-selector" class="mcnb-select"> 110 <option value="24hours" ><?php esc_html_e( 'Últimas 24 horas', 'madnesschat-button' ); ?></option>111 <option value="7days" selected><?php esc_html_e( 'Últimos 7 días', 'madnesschat-button' ); ?></option>112 <option value="30days" ><?php esc_html_e( 'Últimos 30 días', 'madnesschat-button' ); ?></option>113 <option value="90days" ><?php esc_html_e( 'Últimos 90 días', 'madnesschat-button' ); ?></option>110 <option value="24hours" <?php selected( $period, '24hours' ); ?>><?php esc_html_e( 'Últimas 24 horas', 'madnesschat-button' ); ?></option> 111 <option value="7days" <?php selected( $period, '7days' ); ?>><?php esc_html_e( 'Últimos 7 días', 'madnesschat-button' ); ?></option> 112 <option value="30days" <?php selected( $period, '30days' ); ?>><?php esc_html_e( 'Últimos 30 días', 'madnesschat-button' ); ?></option> 113 <option value="90days" <?php selected( $period, '90days' ); ?>><?php esc_html_e( 'Últimos 90 días', 'madnesschat-button' ); ?></option> 114 114 </select> 115 115 <button id="mcnb-export-data" class="button button-secondary" disabled title="<?php echo esc_attr__( 'Esta función está disponible en la versión Pro', 'madnesschat-button' ); ?>"> … … 517 517 $additional_script = " 518 518 jQuery(document).ready(function($) { 519 // Selector de período - siempre funciona 520 $('#mcnb-period-selector').on('change', function() { 521 var period = $(this).val(); 522 var currentUrl = new URL(window.location); 523 currentUrl.searchParams.set('period', period); 524 window.location.href = currentUrl.toString(); 525 }); 526 527 // Sincronizar selector con URL 528 var urlParams = new URLSearchParams(window.location.search); 529 var selectedPeriod = urlParams.get('period') || '7days'; 530 $('#mcnb-period-selector').val(selectedPeriod); 531 532 // Botón de actualizar 533 $('#mcnb-refresh-data').on('click', function() { 534 window.location.reload(); 535 }); 536 537 // Botón de exportar (solo si mcnbDashboard está definido) 519 538 if (typeof mcnbDashboard !== 'undefined') { 520 $('.mcnb-button-test > div > div').on('click', function() {521 $(this).css('transform', 'scale(0.95)');522 setTimeout(function() {523 $(this).css('transform', 'scale(1)');524 }.bind(this), 150);525 });526 527 $('#mcnb-period-selector').on('change', function() {528 var period = $(this).val();529 var currentUrl = new URL(window.location);530 currentUrl.searchParams.set('period', period);531 window.location.href = currentUrl.toString();532 });533 534 var urlParams = new URLSearchParams(window.location.search);535 var selectedPeriod = urlParams.get('period') || '7days';536 $('#mcnb-period-selector').val(selectedPeriod);537 538 $('#mcnb-refresh-data').on('click', function() {539 window.location.reload();540 });541 542 // Función de exportar solo disponible en versión Pro543 539 $('#mcnb-export-data').on('click', function(e) { 544 540 // Verificar si el botón está deshabilitado (versión gratuita) … … 564 560 \$form.remove(); 565 561 }); 562 563 $('.mcnb-button-test > div > div').on('click', function() { 564 $(this).css('transform', 'scale(0.95)'); 565 setTimeout(function() { 566 $(this).css('transform', 'scale(1)'); 567 }.bind(this), 150); 568 }); 566 569 } 567 570 }); -
madnesschat-button/tags/1.0.2/assets/js/mcnb-dashboard.js
r3398221 r3401134 47 47 48 48 loadDashboardData(silent = false) { 49 // Solo ejecutar si estamos en el dashboard completo (tiene elementos específicos) 50 // No ejecutar en el dashboard simple 51 if (!$('#total-clicks').length && !$('#clicks-chart').length) { 52 return; // Dashboard simple - no hacer nada 53 } 54 49 55 if (!silent) { 50 56 this.showLoading(); … … 78 84 showLoading() { 79 85 $('.mcnb-chart-loading').addClass('active'); 80 $('.mcnb-kpi-value').text('-'); 86 // Solo cambiar valores si existen los elementos específicos del dashboard completo 87 // No afectar el dashboard simple que usa .mcnb-kpi-value directamente 88 $('#total-clicks').text('-'); 89 $('#conversion-rate').text('-'); 90 $('#mobile-percentage').text('-'); 91 $('#peak-hour').text('-'); 81 92 } 82 93 … … 100 111 101 112 updateKPIs(data) { 102 // Total Clicks 103 $('#total-clicks').text(this.formatNumber(data.total_clicks)); 104 $('#clicks-change').text(this.formatChange(data.growth_rate.growth_rate)) 105 .removeClass('positive negative neutral') 106 .addClass(this.getChangeClass(data.growth_rate.growth_rate)); 113 // Solo actualizar si los elementos existen (dashboard completo) 114 // No afectar el dashboard simple 115 if ($('#total-clicks').length) { 116 // Total Clicks 117 $('#total-clicks').text(this.formatNumber(data.total_clicks)); 118 } 119 if ($('#clicks-change').length) { 120 $('#clicks-change').text(this.formatChange(data.growth_rate.growth_rate)) 121 .removeClass('positive negative neutral') 122 .addClass(this.getChangeClass(data.growth_rate.growth_rate)); 123 } 107 124 108 125 // Conversion Rate 109 $('#conversion-rate').text(data.conversion_rate.rate + '%'); 126 if ($('#conversion-rate').length) { 127 $('#conversion-rate').text(data.conversion_rate.rate + '%'); 128 } 110 129 111 130 // Mobile Percentage 112 const mobileData = data.device_breakdown.find(d => d.device === 'Mobile'); 113 const mobilePercentage = mobileData ? 114 Math.round((mobileData.clicks / data.total_clicks) * 100) : 0; 115 $('#mobile-percentage').text(mobilePercentage + '%'); 131 if ($('#mobile-percentage').length) { 132 const mobileData = data.device_breakdown.find(d => d.device === 'Mobile'); 133 const mobilePercentage = mobileData ? 134 Math.round((mobileData.clicks / data.total_clicks) * 100) : 0; 135 $('#mobile-percentage').text(mobilePercentage + '%'); 136 } 116 137 117 138 // Peak Hour 118 const peakHour = data.hourly_distribution.reduce((max, hour) => 119 hour.clicks > max.clicks ? hour : max, data.hourly_distribution[0]); 120 $('#peak-hour').text(this.formatHour(peakHour.hour)); 121 $('#peak-clicks').text(peakHour.clicks + ' clicks'); 139 if ($('#peak-hour').length) { 140 const peakHour = data.hourly_distribution.reduce((max, hour) => 141 hour.clicks > max.clicks ? hour : max, data.hourly_distribution[0]); 142 $('#peak-hour').text(this.formatHour(peakHour.hour)); 143 $('#peak-clicks').text(peakHour.clicks + ' clicks'); 144 } 122 145 } 123 146 … … 603 626 604 627 // Initialize dashboard when DOM is ready 628 // Solo inicializar si estamos en el dashboard completo (tiene elementos específicos) 605 629 $(document).ready(() => { 606 new MCNBDashboard(); 630 // Verificar si estamos en el dashboard completo o simple 631 if ($('#total-clicks').length || $('#clicks-chart').length) { 632 // Dashboard completo - inicializar 633 new MCNBDashboard(); 634 } 635 // Dashboard simple - no inicializar, los valores se muestran directamente desde PHP 607 636 }); 608 637 -
madnesschat-button/tags/1.0.2/includes/class-mcnb-analytics.php
r3398221 r3401134 65 65 } 66 66 67 global $wpdb; 68 $table_name = $wpdb->prefix . 'mcnb_analytics'; 69 70 // Recopilar datos del click 67 // Verificar si es versión Pro 68 $is_pro = apply_filters( 'mcnb/is_pro', false ); 69 70 global $wpdb; 71 $table_name = $wpdb->prefix . 'mcnb_analytics'; 72 73 // Datos básicos (siempre se recopilan) 71 74 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 72 75 $page_url = isset( $_POST['page_url'] ) ? esc_url_raw( wp_unslash( $_POST['page_url'] ) ) : ''; 73 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized74 $page_title = isset( $_POST['page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['page_title'] ) ) : '';75 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized76 $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';77 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized78 $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '';79 // Sanitizar y validar button_config (JSON)80 $button_config = '{}';81 if ( isset( $_POST['button_config'] ) ) {82 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized83 $config_raw = wp_unslash( $_POST['button_config'] );84 // Si es un string JSON, decodificar, sanitizar y volver a codificar85 if ( is_string( $config_raw ) ) {86 $config_decoded = json_decode( $config_raw, true );87 if ( json_last_error() === JSON_ERROR_NONE && is_array( $config_decoded ) ) {88 // Sanitizar cada campo del array89 $config_sanitized = array();90 foreach ( $config_decoded as $key => $value ) {91 $sanitized_key = sanitize_key( $key );92 if ( is_string( $value ) ) {93 $config_sanitized[ $sanitized_key ] = sanitize_text_field( $value );94 } elseif ( is_array( $value ) ) {95 $config_sanitized[ $sanitized_key ] = map_deep( $value, 'sanitize_text_field' );96 } else {97 $config_sanitized[ $sanitized_key ] = $value;98 }99 }100 $button_config = wp_json_encode( $config_sanitized );101 }102 } elseif ( is_array( $config_raw ) ) {103 // Si ya es un array, sanitizar directamente104 $config_sanitized = map_deep( $config_raw, 'sanitize_text_field' );105 $button_config = wp_json_encode( $config_sanitized );106 }107 }108 76 109 77 $data = array( 110 78 'click_date' => current_time( 'mysql' ), 111 79 'page_url' => $page_url, 112 'page_title' => $page_title,113 'user_agent' => $user_agent,114 'ip_address' => MCNB_Security::get_client_ip(),115 'referrer' => $referrer,116 'device_type' => self::detect_device_type(),117 'browser' => self::detect_browser(),118 'button_config' => $button_config119 80 ); 120 81 121 // Detectar país por IP (opcional) 122 $data['country'] = self::detect_country( $data['ip_address'] ); 82 // Solo recopilar datos avanzados si es versión Pro 83 if ( $is_pro ) { 84 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 85 $page_title = isset( $_POST['page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['page_title'] ) ) : ''; 86 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 87 $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; 88 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 89 $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : ''; 90 91 // Sanitizar y validar button_config (JSON) 92 $button_config = '{}'; 93 if ( isset( $_POST['button_config'] ) ) { 94 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 95 $config_raw = wp_unslash( $_POST['button_config'] ); 96 // Si es un string JSON, decodificar, sanitizar y volver a codificar 97 if ( is_string( $config_raw ) ) { 98 $config_decoded = json_decode( $config_raw, true ); 99 if ( json_last_error() === JSON_ERROR_NONE && is_array( $config_decoded ) ) { 100 // Sanitizar cada campo del array 101 $config_sanitized = array(); 102 foreach ( $config_decoded as $key => $value ) { 103 $sanitized_key = sanitize_key( $key ); 104 if ( is_string( $value ) ) { 105 $config_sanitized[ $sanitized_key ] = sanitize_text_field( $value ); 106 } elseif ( is_array( $value ) ) { 107 $config_sanitized[ $sanitized_key ] = map_deep( $value, 'sanitize_text_field' ); 108 } else { 109 $config_sanitized[ $sanitized_key ] = $value; 110 } 111 } 112 $button_config = wp_json_encode( $config_sanitized ); 113 } 114 } elseif ( is_array( $config_raw ) ) { 115 // Si ya es un array, sanitizar directamente 116 $config_sanitized = map_deep( $config_raw, 'sanitize_text_field' ); 117 $button_config = wp_json_encode( $config_sanitized ); 118 } 119 } 120 121 // Datos avanzados solo para Pro 122 $data['page_title'] = $page_title; 123 $data['user_agent'] = $user_agent; 124 $data['ip_address'] = MCNB_Security::get_client_ip(); 125 $data['referrer'] = $referrer; 126 $data['device_type'] = self::detect_device_type(); 127 $data['browser'] = self::detect_browser(); 128 $data['button_config'] = $button_config; 129 130 // Detectar país por IP (solo Pro) 131 $data['country'] = self::detect_country( $data['ip_address'] ); 132 } else { 133 // Versión gratuita: solo datos mínimos 134 $data['page_title'] = ''; 135 $data['user_agent'] = ''; 136 $data['ip_address'] = ''; 137 $data['referrer'] = ''; 138 $data['device_type'] = ''; 139 $data['browser'] = ''; 140 $data['button_config'] = '{}'; 141 $data['country'] = ''; 142 } 123 143 124 144 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -
madnesschat-button/tags/1.0.2/includes/class-mcnb-settings.php
r3398221 r3401134 274 274 add_settings_section( 275 275 'mcnb_section_basic', 276 __( ' Basic Settings', 'madnesschat-button' ),277 function() { echo '<p>' . esc_html__( 'Configur e your WhatsApp button.', 'madnesschat-button' ) . '</p>'; },276 __( 'Configuración básica', 'madnesschat-button' ), 277 function() { echo '<p>' . esc_html__( 'Configurá tu botón de WhatsApp.', 'madnesschat-button' ) . '</p>'; }, 278 278 'mcnb_basic' 279 279 ); 280 280 281 self::add_field( 'main_config', __( 'Configuración Principal', 'madnesschat-button' ), function() {281 self::add_field( 'main_config', __( 'Configuración básica', 'madnesschat-button' ), function() { 282 282 $options = self::get_options(); 283 283 echo '<div class="mcnb-field-group">'; … … 288 288 echo '<label class="mcnb-field-label">' . esc_html__( 'Número de WhatsApp', 'madnesschat-button' ) . '</label>'; 289 289 echo '<input type="text" name="mcnb_basic_options[phone_number]" value="' . esc_attr( $options['phone_number'] ) . '" class="mcnb-input mcnb-phone-input" placeholder="5491122334455" />'; 290 printf( '<p class="description">%s</p>', esc_html__( 'Ingres ael número con código de país, sin + ni espacios. Ejemplo: 5491122334455', 'madnesschat-button' ) );290 printf( '<p class="description">%s</p>', esc_html__( 'Ingresá el número con código de país, sin + ni espacios. Ejemplo: 5491122334455', 'madnesschat-button' ) ); 291 291 echo '</div>'; 292 292 echo '</div>'; … … 304 304 echo '<div class="mcnb-field-row">'; 305 305 echo '<div class="mcnb-field-col">'; 306 echo '<label class="mcnb-field-label">' . esc_html__( ' Posición en pantalla', 'madnesschat-button' ) . '</label>';306 echo '<label class="mcnb-field-label">' . esc_html__( 'Ajustá la posición', 'madnesschat-button' ) . '</label>'; 307 307 echo '<select name="mcnb_basic_options[position]" class="mcnb-select">'; 308 308 foreach ( array( 'bottom-right' => __( 'Abajo derecha', 'madnesschat-button' ), 'bottom-left' => __( 'Abajo izquierda', 'madnesschat-button' ) ) as $value => $label ) { … … 320 320 echo '</div>'; 321 321 echo '</div>'; 322 printf( '<p class="description">%s</p>', esc_html__( 'Configur ael número, mensaje y posición del botón en tu sitio web.', 'madnesschat-button' ) );322 printf( '<p class="description">%s</p>', esc_html__( 'Configurá el número, mensaje y posición del botón en tu sitio web.', 'madnesschat-button' ) ); 323 323 echo '</div>'; 324 324 }, 'mcnb_section_basic', 'mcnb_basic' ); … … 337 337 echo '<span class="mcnb-toggle-slider"></span>'; 338 338 echo '</div>'; 339 echo esc_html__( 'Configur ación responsivepor dispositivo', 'madnesschat-button' );339 echo esc_html__( 'Configurá tamaños diferentes por dispositivo', 'madnesschat-button' ); 340 340 echo '</label>'; 341 341 echo '<p class="mcnb-field-hint">' . esc_html__( 'Permite configurar diferentes tamaños y posiciones para móvil, tablet, laptop y desktop.', 'madnesschat-button' ) . '</p>'; … … 346 346 echo '<div id="mcnb_basic_size" class="mcnb-field-row">'; 347 347 echo '<div class="mcnb-field-col">'; 348 echo '<label class="mcnb-field-label">' . esc_html__( ' Tamaño del botón', 'madnesschat-button' ) . '</label>';348 echo '<label class="mcnb-field-label">' . esc_html__( 'Elegí el tamaño', 'madnesschat-button' ) . '</label>'; 349 349 echo '<div class="mcnb-size-options">'; 350 350 $values = array( '48' => '48px', '56' => '56px', '64' => '64px', '72' => '72px', '80' => '80px' ); … … 377 377 echo '<div class="mcnb-field-row">'; 378 378 echo '<div class="mcnb-field-col">'; 379 echo '<label class="mcnb-field-label">' . esc_html__( ' Color de fondo', 'madnesschat-button' ) . '</label>';379 echo '<label class="mcnb-field-label">' . esc_html__( 'Elegí el color de fondo', 'madnesschat-button' ) . '</label>'; 380 380 echo '<input type="text" name="mcnb_basic_options[brand_color]" value="' . esc_attr( $options['brand_color'] ) . '" class="mcnb-color" />'; 381 381 echo '<p class="mcnb-field-hint">' . esc_html__( 'Color principal del botón', 'madnesschat-button' ) . '</p>'; 382 382 echo '</div>'; 383 383 echo '<div class="mcnb-field-col">'; 384 echo '<label class="mcnb-field-label">' . esc_html__( ' Color del ícono/texto', 'madnesschat-button' ) . '</label>';384 echo '<label class="mcnb-field-label">' . esc_html__( 'Elegí el color del ícono', 'madnesschat-button' ) . '</label>'; 385 385 echo '<input type="text" name="mcnb_basic_options[text_color]" value="' . esc_attr( $options['text_color'] ) . '" class="mcnb-color" />'; 386 386 echo '<p class="mcnb-field-hint">' . esc_html__( 'Color del ícono y texto', 'madnesschat-button' ) . '</p>'; … … 412 412 ), 413 413 'custom' => array( 414 'label' => __( 'Personaliz ado', 'madnesschat-button' ),414 'label' => __( 'Personalizá tu ícono', 'madnesschat-button' ), 415 415 'icon' => '<span style="font-size:20px;">🔧</span>' 416 416 ) … … 437 437 echo '<label class="mcnb-field-label">' . esc_html__( 'URL del ícono personalizado', 'madnesschat-button' ) . '</label>'; 438 438 echo '<input type="url" name="mcnb_basic_options[custom_icon]" value="' . esc_attr( $options['custom_icon'] ) . '" class="mcnb-input" placeholder="https://ejemplo.com/mi-icono.svg" />'; 439 echo '<p class="description">' . esc_html__( 'Ingres ala URL de una imagen (SVG, PNG, JPG) o código HTML personalizado.', 'madnesschat-button' ) . '</p>';439 echo '<p class="description">' . esc_html__( 'Ingresá la URL de una imagen (SVG, PNG, JPG) o código HTML personalizado.', 'madnesschat-button' ) . '</p>'; 440 440 echo '</div>'; 441 441 echo '</div>'; … … 444 444 echo '<div id="mcnb_responsive_config" class="mcnb-field-row" style="display:none;">'; 445 445 echo '<div class="mcnb-field-col-full">'; 446 echo '<h4 style="margin: 0 0 16px 0; color: #333;">' . esc_html__( 'Configur ación por Dispositivo', 'madnesschat-button' ) . '</h4>';446 echo '<h4 style="margin: 0 0 16px 0; color: #333;">' . esc_html__( 'Configurá por dispositivo', 'madnesschat-button' ) . '</h4>'; 447 447 448 448 $devices = array( … … 507 507 echo '<span class="mcnb-toggle-slider"></span>'; 508 508 echo '</div>'; 509 echo esc_html__( 'Mostr aretiqueta', 'madnesschat-button' );509 echo esc_html__( 'Mostrá la etiqueta', 'madnesschat-button' ); 510 510 echo '</label>'; 511 511 echo '</div>'; … … 519 519 echo '</div>'; 520 520 521 printf( '<p class="description">%s</p>', esc_html__( 'Configur ael tamaño, estilo, colores e ícono del botón para que se adapte a tu marca.', 'madnesschat-button' ) );521 printf( '<p class="description">%s</p>', esc_html__( 'Configurá el tamaño, estilo, colores e ícono del botón para que se adapte a tu marca.', 'madnesschat-button' ) ); 522 522 echo '</div>'; 523 523 }, 'mcnb_section_basic', 'mcnb_basic' ); … … 533 533 echo '<span class="mcnb-toggle-slider"></span>'; 534 534 echo '</div>'; 535 echo esc_html__( 'Mostr aretiqueta de texto', 'madnesschat-button' );535 echo esc_html__( 'Mostrá la etiqueta de texto', 'madnesschat-button' ); 536 536 echo '</label>'; 537 537 echo '</div>'; … … 543 543 echo '</div>'; 544 544 echo '<div class="mcnb-field-col">'; 545 echo '<label class="mcnb-field-label">' . esc_html__( ' Posición de la etiqueta', 'madnesschat-button' ) . '</label>';545 echo '<label class="mcnb-field-label">' . esc_html__( 'Ajustá la posición de la etiqueta', 'madnesschat-button' ) . '</label>'; 546 546 $positions = array( 547 547 'left' => __( 'Izquierda', 'madnesschat-button' ), … … 573 573 echo '<span class="mcnb-toggle-slider"></span>'; 574 574 echo '</div>'; 575 echo esc_html__( 'Activ arsombra', 'madnesschat-button' );575 echo esc_html__( 'Activá la sombra', 'madnesschat-button' ); 576 576 echo '</label>'; 577 577 echo '<p class="mcnb-field-hint">' . esc_html__( 'Añade profundidad visual al botón', 'madnesschat-button' ) . '</p>'; … … 583 583 echo '<span class="mcnb-toggle-slider"></span>'; 584 584 echo '</div>'; 585 echo esc_html__( 'Activ arborde', 'madnesschat-button' );585 echo esc_html__( 'Activá el borde', 'madnesschat-button' ); 586 586 echo '</label>'; 587 587 echo '<p class="mcnb-field-hint">' . esc_html__( 'Contorno decorativo alrededor del botón', 'madnesschat-button' ) . '</p>'; … … 592 592 echo '<div class="mcnb-field-row mcnb-border-options" style="display:none;">'; 593 593 echo '<div class="mcnb-field-col">'; 594 echo '<label class="mcnb-field-label">' . esc_html__( ' Grosor del borde (px)', 'madnesschat-button' ) . '</label>';594 echo '<label class="mcnb-field-label">' . esc_html__( 'Ajustá el grosor del borde', 'madnesschat-button' ) . '</label>'; 595 595 echo '<input type="number" name="mcnb_basic_options[border_width]" value="' . esc_attr( $options['border_width'] ) . '" min="1" max="10" class="small-text" />'; 596 596 echo '</div>'; 597 597 echo '<div class="mcnb-field-col">'; 598 echo '<label class="mcnb-field-label">' . esc_html__( ' Color del borde', 'madnesschat-button' ) . '</label>';598 echo '<label class="mcnb-field-label">' . esc_html__( 'Elegí el color del borde', 'madnesschat-button' ) . '</label>'; 599 599 echo '<input type="text" name="mcnb_basic_options[border_color]" value="' . esc_attr( $options['border_color'] ) . '" class="mcnb-color" />'; 600 600 echo '</div>'; … … 629 629 // Efectos hover 630 630 echo '<div class="mcnb-field-row">'; 631 echo '<div class="mcnb-field-col ">';631 echo '<div class="mcnb-field-col-full">'; 632 632 echo '<label class="mcnb-field-label">' . esc_html__( 'Efecto al pasar el mouse', 'madnesschat-button' ) . '</label>'; 633 633 $hovers = array( … … 645 645 echo '<p class="mcnb-field-hint">' . esc_html__( 'Interacción visual cuando el usuario pasa el mouse', 'madnesschat-button' ) . '</p>'; 646 646 echo '</div>'; 647 echo '<div class="mcnb-field-col">'; 648 echo '<div class="mcnb-effects-preview" style="padding:20px;background:#f8f9fa;border-radius:8px;text-align:center;">'; 649 echo '<div class="mcnb-effect-demo" style="width:50px;height:50px;background:#25D366;border-radius:50%;margin:0 auto;display:flex;align-items:center;justify-content:center;color:white;cursor:pointer;transition:all 0.2s ease;">'; 650 echo '<span style="font-size:20px;">💬</span>'; 651 echo '</div>'; 652 echo '<p style="margin:8px 0 0;font-size:12px;color:#6c757d;">' . esc_html__( 'Preview del efecto', 'madnesschat-button' ) . '</p>'; 653 echo '</div>'; 654 echo '</div>'; 655 echo '</div>'; 656 657 printf( '<p class="description">%s</p>', esc_html__( 'Configura sombras, bordes, animaciones y efectos hover para hacer el botón más atractivo.', 'madnesschat-button' ) ); 647 echo '</div>'; 648 649 printf( '<p class="description">%s</p>', esc_html__( 'Configurá sombras, bordes, animaciones y efectos hover para hacer el botón más atractivo.', 'madnesschat-button' ) ); 658 650 echo '</div>'; 659 651 }, 'mcnb_section_basic', 'mcnb_basic' ); … … 661 653 add_settings_section( 662 654 'mcnb_section_visibility', 663 __( 'Visibili ty and Targeting', 'madnesschat-button' ),664 function() { echo '<p>' . esc_html__( 'Control where and when the button is displayed.', 'madnesschat-button' ) . '</p>'; },655 __( 'Visibilidad y targeting', 'madnesschat-button' ), 656 function() { echo '<p>' . esc_html__( 'Controlá dónde y cuándo se muestra el botón.', 'madnesschat-button' ) . '</p>'; }, 665 657 'mcnb_visibility' 666 658 ); … … 744 736 self::add_field( 'schedule', __( 'Horario de atención', 'madnesschat-button' ), function() { 745 737 $options = self::get_options(); 746 echo '<label><input type="checkbox" name="mcnb_basic_options[schedule_enabled]" value="1" ' . checked( $options['schedule_enabled'], true, false ) . ' /> ' . esc_html__( 'Activ arhorario', 'madnesschat-button' ) . '</label><br />';738 echo '<label><input type="checkbox" name="mcnb_basic_options[schedule_enabled]" value="1" ' . checked( $options['schedule_enabled'], true, false ) . ' /> ' . esc_html__( 'Activá el horario', 'madnesschat-button' ) . '</label><br />'; 747 739 echo '<input type="time" name="mcnb_basic_options[schedule_start]" value="' . esc_attr( $options['schedule_start'] ) . '" /> - '; 748 740 echo '<input type="time" name="mcnb_basic_options[schedule_end]" value="' . esc_attr( $options['schedule_end'] ) . '" />'; … … 751 743 add_settings_section( 752 744 'mcnb_section_privacy', 753 __( 'Privac y and Analytics', 'madnesschat-button' ),754 function() { echo '<p>' . esc_html__( 'Consent and measurement.', 'madnesschat-button' ) . '</p>'; },745 __( 'Privacidad y analíticas', 'madnesschat-button' ), 746 function() { echo '<p>' . esc_html__( 'Consentimiento y medición.', 'madnesschat-button' ) . '</p>'; }, 755 747 'mcnb_privacy' 756 748 ); 757 749 750 self::add_field( 'analytics', __( 'Analytics Básico', 'madnesschat-button' ), function() { 751 $options = self::get_options(); 752 $is_pro = apply_filters( 'mcnb/is_pro', false ); 753 echo '<div class="mcnb-field-group">'; 754 echo '<div class="mcnb-field-row">'; 755 echo '<div class="mcnb-field-col-full">'; 756 echo '<label class="mcnb-toggle-label">'; 757 echo '<div class="mcnb-toggle">'; 758 echo '<input type="checkbox" name="mcnb_basic_options[analytics_enabled]" value="1" ' . checked( $options['analytics_enabled'], true, false ) . ' />'; 759 echo '<span class="mcnb-toggle-slider"></span>'; 760 echo '</div>'; 761 echo esc_html__( 'Activá el tracking de clicks', 'madnesschat-button' ); 762 echo '</label>'; 763 echo '<p class="mcnb-field-hint">' . esc_html__( 'Versión gratuita: Solo registra fecha/hora y URL básica para contar clicks. Versión Pro: Recopila datos detallados (dispositivo, navegador, país, referrer, etc.) para análisis avanzados.', 'madnesschat-button' ) . '</p>'; 764 if ( ! $is_pro ) { 765 echo '<div style="margin-top: 12px; padding: 12px; background: #f8f9fa; border-left: 3px solid #667eea; border-radius: 4px;">'; 766 echo '<p style="margin: 0; font-size: 13px; color: #495057;">'; 767 echo '<strong>' . esc_html__( '💡 Versión Pro:', 'madnesschat-button' ) . '</strong> '; 768 echo esc_html__( 'Desbloqueá analytics avanzados con reportes detallados, exportación de datos, análisis por dispositivo/navegador/ubicación e insights automáticos.', 'madnesschat-button' ); 769 echo ' <a href="mailto:[email protected]?subject=Solicito%20Versión%20Pro" style="color: #667eea; text-decoration: none;">' . esc_html__( 'Conocer más →', 'madnesschat-button' ) . '</a>'; 770 echo '</p>'; 771 echo '</div>'; 772 } 773 echo '</div>'; 774 echo '</div>'; 775 echo '</div>'; 776 }, 'mcnb_section_privacy', 'mcnb_privacy' ); 777 758 778 self::add_field( 'gdpr', __( 'GDPR', 'madnesschat-button' ), function() { 759 779 $options = self::get_options(); 760 echo '<label><input type="checkbox" name="mcnb_basic_options[gdpr_enabled]" value="1" ' . checked( $options['gdpr_enabled'], true, false ) . ' /> ' . esc_html__( 'Solicitar consentimiento antes de abrir WhatsApp', 'madnesschat-button' ) . '</label><br /><br />'; 761 762 echo '<div style="margin-top: 15px;">'; 763 echo '<label style="display: block; margin-bottom: 5px; font-weight: 600;">' . esc_html__( 'Mensaje de consentimiento:', 'madnesschat-button' ) . '</label>'; 780 echo '<div class="mcnb-field-group">'; 781 echo '<div class="mcnb-field-row">'; 782 echo '<div class="mcnb-field-col-full">'; 783 echo '<label class="mcnb-toggle-label">'; 784 echo '<div class="mcnb-toggle">'; 785 echo '<input type="checkbox" name="mcnb_basic_options[gdpr_enabled]" value="1" ' . checked( $options['gdpr_enabled'], true, false ) . ' />'; 786 echo '<span class="mcnb-toggle-slider"></span>'; 787 echo '</div>'; 788 echo esc_html__( 'Solicitá consentimiento antes de abrir WhatsApp', 'madnesschat-button' ); 789 echo '</label>'; 790 echo '</div>'; 791 echo '</div>'; 792 793 echo '<div class="mcnb-field-row">'; 794 echo '<div class="mcnb-field-col-full">'; 795 echo '<label class="mcnb-field-label">' . esc_html__( 'Mensaje de consentimiento', 'madnesschat-button' ) . '</label>'; 764 796 echo '<input type="text" name="mcnb_basic_options[gdpr_message]" value="' . esc_attr( $options['gdpr_message'] ) . '" class="large-text" placeholder="' . esc_attr__( 'Usamos WhatsApp para atenderte. ¿Aceptas continuar?', 'madnesschat-button' ) . '" />'; 765 797 echo '</div>'; 766 767 echo '<div style="margin-top: 15px;">'; 768 echo '<label style="display: block; margin-bottom: 5px; font-weight: 600;">' . esc_html__( 'Texto del botón de aceptar:', 'madnesschat-button' ) . '</label>'; 798 echo '</div>'; 799 800 echo '<div class="mcnb-field-row">'; 801 echo '<div class="mcnb-field-col-full">'; 802 echo '<label class="mcnb-field-label">' . esc_html__( 'Texto del botón de aceptar', 'madnesschat-button' ) . '</label>'; 769 803 echo '<input type="text" name="mcnb_basic_options[gdpr_button_label]" value="' . esc_attr( $options['gdpr_button_label'] ) . '" class="regular-text" placeholder="' . esc_attr__( 'Aceptar y continuar', 'madnesschat-button' ) . '" />'; 770 804 echo '</div>'; 805 echo '</div>'; 806 echo '</div>'; 771 807 }, 'mcnb_section_privacy', 'mcnb_privacy' ); 772 808 773 809 self::add_field( 'utm', __( 'UTM', 'madnesschat-button' ), function() { 774 810 $options = self::get_options(); 775 echo '<label><input type="checkbox" name="mcnb_basic_options[utm_enabled]" value="1" ' . checked( $options['utm_enabled'], true, false ) . ' /> ' . esc_html__( 'Añad irparámetros UTM al enlace', 'madnesschat-button' ) . '</label><br />';811 echo '<label><input type="checkbox" name="mcnb_basic_options[utm_enabled]" value="1" ' . checked( $options['utm_enabled'], true, false ) . ' /> ' . esc_html__( 'Añadí parámetros UTM al enlace', 'madnesschat-button' ) . '</label><br />'; 776 812 echo '<input type="text" name="mcnb_basic_options[utm_source]" value="' . esc_attr( $options['utm_source'] ) . '" class="small-text" /> '; 777 813 echo '<input type="text" name="mcnb_basic_options[utm_medium]" value="' . esc_attr( $options['utm_medium'] ) . '" class="small-text" /> '; … … 782 818 add_settings_section( 783 819 'mcnb_section_advanced', 784 __( ' Advanced Features', 'madnesschat-button' ),785 function() { echo '<p>' . esc_html__( ' Premium features to maximize conversions.', 'madnesschat-button' ) . '</p>'; },820 __( 'Funciones avanzadas', 'madnesschat-button' ), 821 function() { echo '<p>' . esc_html__( 'Funciones premium para maximizar conversiones.', 'madnesschat-button' ) . '</p>'; }, 786 822 'mcnb_advanced' 787 823 ); … … 794 830 echo '<div class="mcnb-pro-feature-content">'; 795 831 echo '<h3>' . esc_html__( 'Múltiples Agentes', 'madnesschat-button' ) . '</h3>'; 796 echo '<p>' . esc_html__( 'Configur avarios números de WhatsApp con rotación automática según horarios de disponibilidad.', 'madnesschat-button' ) . '</p>';832 echo '<p>' . esc_html__( 'Configurá varios números de WhatsApp con rotación automática según horarios de disponibilidad.', 'madnesschat-button' ) . '</p>'; 797 833 echo '<ul class="mcnb-feature-benefits">'; 798 834 echo '<li><span class="dashicons dashicons-yes"></span> ' . esc_html__( 'Múltiples números de WhatsApp', 'madnesschat-button' ) . '</li>'; -
madnesschat-button/tags/1.0.2/madnesschat-button.php
r3398410 r3401134 3 3 * Plugin Name: MadnessChat Button 4 4 * Description: Easy-to-configure floating WhatsApp button with GDPR compliance, basic analytics, and smart triggers. 5 * Version: 1.0. 25 * Version: 1.0.3 6 6 * Author: madnesscode1 7 7 * Requires at least: 5.6 … … 39 39 } 40 40 41 define( 'MCNB_VERSION', '1.0. 2' );41 define( 'MCNB_VERSION', '1.0.3' ); 42 42 define( 'MCNB_SLUG', 'madnesschat-button' ); 43 43 define( 'MCNB_FILE', __FILE__ ); -
madnesschat-button/tags/1.0.2/readme.txt
r3399016 r3401134 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.3 7 Stable tag: 1.0. 27 Stable tag: 1.0.3 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Easy-to-configure floating WhatsApp button for WordPress. Perfect for contacting your visitors.11 Add a customizable WhatsApp chat button to your WordPress site — 4 styles, 6 animations, responsive sizes by device, GDPR-ready. 12 12 13 13 == Description == 14 15 This plugin helps you add a WhatsApp floating button, WhatsApp contact widget, or WhatsApp chat icon without coding. Ideal for small businesses looking to increase conversions and customer support. 14 16 15 17 **MadnessChat Button** is a simple and free solution to integrate a WhatsApp chat button into your WordPress website. Perfect for blogs, small businesses, and personal projects. … … 127 129 == Changelog == 128 130 131 = 1.0.3 = 132 * SEO optimization: Updated short description with keywords (WhatsApp button, WhatsApp chat, WhatsApp widget) 133 * Added SEO paragraph in description section for better WordPress.org search visibility 134 * Added review call-to-action: Footer message and admin notice after saving settings 135 * UI improvements: Removed duplicate title, unified style to friendly Spanish (informal tone) 136 * Removed duplicate preview in Visual Effects section (already shown in sidebar) 137 * Fixed dashboard statistics: Prevented values from disappearing due to JavaScript conflicts 138 * Added Analytics field in Privacy tab with clear Basic vs Pro separation 139 * Optimized data collection: Free version only collects essential data (date, URL), Pro version collects detailed analytics (device, browser, country, IP, etc.) 140 * Improved dashboard period selector synchronization 141 * Enhanced user experience with consistent informal Spanish language throughout admin panel 142 129 143 = 1.0.2 = 130 144 * Changed all prefixes from "mcnb" to "mcnb" to comply with WordPress naming conventions (minimum 4 characters) … … 143 157 == Upgrade Notice == 144 158 159 = 1.0.3 = 160 SEO and UX improvements update. Better search visibility, review prompts, improved dashboard stability, and optimized data collection. Free version now collects minimal data for privacy. Recommended update for all users. 161 145 162 = 1.0.2 = 146 163 Important WordPress.org compliance update. Prefixes updated to meet naming standards. Enhanced security and code quality. No breaking changes - all functionality remains the same.
Note: See TracChangeset
for help on using the changeset viewer.