Plugin Directory

Changeset 3341621


Ignore:
Timestamp:
08/08/2025 12:13:02 PM (8 months ago)
Author:
superfrete
Message:

Update to version 3.2.1

Location:
superfrete/trunk
Files:
21 added
18 edited

Legend:

Unmodified
Added
Removed
  • superfrete/trunk

    • Property svn:ignore set to
      .direnv
      .gitignore
      .gitignore-dev
      .env
      .env.local
      node_modules
      composer.lock
      *.log
      *.sh
      .DS_Store
      Thumbs.db
      docker-compose.yml
      Dockerfile
  • superfrete/trunk/api/Http/Request.php

    r3289983 r3341621  
    1414     */
    1515    public function __construct() {
    16         $this->api_url = 'https://api.superfrete.com'; // URL padrão da API
    17         // Verifica se o sandbox está ativado e troca o token
    18         $sandbox_enabled = get_option('superfrete_sandbox_mode') === 'yes';
    19         $this->api_token = $sandbox_enabled ? get_option('superfrete_api_token_sandbox') : get_option('superfrete_api_token');
    20 
    21         if ($sandbox_enabled) {
    22             $this->api_url = 'https://sandbox.superfrete.com'; // URL do ambiente de teste
    23         }
     16        // Set API URL based on environment
     17        $use_dev_env = get_option('superfrete_sandbox_mode') === 'yes';
     18       
     19        if ($use_dev_env) {
     20            $this->api_url = 'https://sandbox.superfrete.com/';
     21            $this->api_token = get_option('superfrete_api_token_sandbox');
     22        } else {
     23            $this->api_url = 'https://api.superfrete.com/';
     24            $this->api_token = get_option('superfrete_api_token');
     25        }
     26       
     27        // Debug logging
     28        error_log('SuperFrete Request: API URL = ' . $this->api_url);
     29        error_log('SuperFrete Request: Token present = ' . (!empty($this->api_token) ? 'yes' : 'no'));
     30        error_log('SuperFrete Request: Use dev env = ' . ($use_dev_env ? 'yes' : 'no'));
    2431    }
    2532
     
    2936    public function call_superfrete_api($endpoint, $method = 'GET', $payload = [], $retorno = false) {
    3037       
     38        // Enhanced debug logging
     39        $environment = (strpos($this->api_url, 'sandbox') !== false || strpos($this->api_url, 'dev') !== false) ? 'SANDBOX/DEV' : 'PRODUCTION';
     40        $full_url = $this->api_url . $endpoint;
     41        $token_preview = !empty($this->api_token) ? substr($this->api_token, 0, 8) . '...' . substr($this->api_token, -4) : 'EMPTY';
     42       
     43        Logger::log('SuperFrete', "API CALL [{$environment}]: {$method} {$full_url}");
     44        Logger::log('SuperFrete', "TOKEN USADO [{$environment}]: {$token_preview}");
     45       
     46        if (empty($this->api_token)) {
     47            Logger::log('SuperFrete', 'API token is empty - cannot make API call');
     48            return false;
     49        }
     50       
    3151        try {
    32              $headers = [
    33             'Content-Type' => 'application/json',
    34             'Accept' => 'application/json',
    35             'Authorization' => 'Bearer ' . $this->api_token ,
    36             'Platform' => 'Woocommerce SuperFrete',
     52            $headers = [
     53                'Content-Type' => 'application/json',
     54                'Accept' => 'application/json',
     55                'Authorization' => 'Bearer ' . $this->api_token,
     56                'User-Agent' => 'WooCommerce SuperFrete Plugin (github.com/superfrete/woocommerce)',
     57                'Platform' => 'Woocommerce SuperFrete',
     58            ];
     59
     60            $params = [
     61                'headers' => $headers,
     62                'method' => $method,
     63                'timeout' => 30, // Increased timeout to 30 seconds
     64            ];
     65
     66            if ($method === 'POST' && !empty($payload)) {
     67                $params['body'] = wp_json_encode($payload);
     68                error_log('SuperFrete API Payload: ' . wp_json_encode($payload));
     69            }
     70
     71            $start_time = microtime(true);
     72            $response = ($method === 'POST') ? wp_remote_post($this->api_url . $endpoint, $params) : wp_remote_get($this->api_url . $endpoint, $params);
     73            $end_time = microtime(true);
     74            $request_time = round(($end_time - $start_time) * 1000, 2);
     75           
     76            error_log('SuperFrete API Request Time: ' . $request_time . ' ms');
     77
     78            // Check for WP errors first
     79            if (is_wp_error($response)) {
     80                $error_message = $response->get_error_message();
     81                Logger::log('SuperFrete', "WP Error na API ({$endpoint}): " . $error_message);
     82                error_log('SuperFrete API WP Error: ' . $error_message);
     83                return false;
     84            }
     85
     86            $status_code = wp_remote_retrieve_response_code($response);
     87            $raw_body = wp_remote_retrieve_body($response);
     88           
     89            // Debug logging
     90            error_log('SuperFrete API Response: Status = ' . $status_code);
     91            error_log('SuperFrete API Response: Body = ' . substr($raw_body, 0, 500) . (strlen($raw_body) > 500 ? '...' : ''));
     92
     93            // Check for HTTP errors
     94            if (!in_array($status_code, [200, 201, 204])) {
     95                $error_msg = "ERRO NA API ({$endpoint}): CÓDIGO {$status_code}";
     96               
     97                // Special handling for 401 errors
     98                if ($status_code == 401) {
     99                    $error_msg .= " - NÃO AUTENTICADO!";
     100                    Logger::log('SuperFrete', $error_msg);
     101                    Logger::log('SuperFrete', "DETALHES [{$environment}]: URL={$full_url}, TOKEN={$token_preview}");
     102                    Logger::log('SuperFrete', "RESPOSTA: " . (strlen($raw_body) > 200 ? substr($raw_body, 0, 200) . '...' : $raw_body));
     103                } else {
     104                    Logger::log('SuperFrete', $error_msg . "\nDETALHES: " . (strlen($raw_body) > 200 ? substr($raw_body, 0, 200) . '...' : ($raw_body ?: 'SEM DETALHES')));
     105                }
     106               
     107                return false;
     108            }
     109
     110            // Handle empty responses (common for DELETE operations)
     111            if (empty($raw_body) && $status_code == 204) {
     112                Logger::log('SuperFrete', "API call successful ({$endpoint}) - No content returned (HTTP {$status_code})");
     113                return true; // Success for DELETE operations
     114            }
     115           
     116            $body = json_decode($raw_body, true);
     117           
     118            // Check for JSON decode errors only if there's content to decode
     119            if (!empty($raw_body) && json_last_error() !== JSON_ERROR_NONE) {
     120                Logger::log('SuperFrete', "JSON decode error na API ({$endpoint}): " . json_last_error_msg() . " - Raw response: " . substr($raw_body, 0, 200));
     121                error_log('SuperFrete API JSON Error: ' . json_last_error_msg());
     122                return false;
     123            }
     124
     125            // Check for API-level errors
     126            if (isset($body['success']) && $body['success'] === false) {
     127                $error_message = isset($body['message']) ? $body['message'] : 'Erro desconhecido';
     128                $errors = $this->extract_api_errors($body);
     129                Logger::log('SuperFrete', "API Error ({$endpoint}): {$error_message}\nDetalhes: {$errors}");
     130                error_log('SuperFrete API Error: ' . $error_message . ' - Details: ' . $errors);
     131                return false;
     132            }
     133
     134            Logger::log('SuperFrete', "API call successful ({$endpoint}) - Time: {$request_time}ms");
     135            return $body;
     136
     137        } catch (Exception $exc) {
     138            Logger::log('SuperFrete', "Exception na API ({$endpoint}): " . $exc->getMessage());
     139            error_log('SuperFrete API Exception: ' . $exc->getMessage());
     140            return false;
     141        }
     142    }
     143
     144    /**
     145     * Extract error details from API response
     146     */
     147    private function extract_api_errors($body) {
     148        $errors = [];
     149       
     150        if (isset($body['errors'])) {
     151            foreach ($body['errors'] as $field => $field_errors) {
     152                if (is_array($field_errors)) {
     153                    $errors[] = $field . ': ' . implode(', ', $field_errors);
     154                } else {
     155                    $errors[] = $field . ': ' . $field_errors;
     156                }
     157            }
     158        } elseif (isset($body['error'])) {
     159            if (is_array($body['error'])) {
     160                foreach ($body['error'] as $error) {
     161                    if (is_array($error)) {
     162                        $errors[] = implode(', ', $error);
     163                    } else {
     164                        $errors[] = $error;
     165                    }
     166                }
     167            } else {
     168                $errors[] = $body['error'];
     169            }
     170        }
     171       
     172        return empty($errors) ? 'Sem detalhes' : implode('; ', $errors);
     173    }
     174
     175    /**
     176     * Register webhook with SuperFrete API
     177     */
     178    public function register_webhook($webhook_url, $events = ['order.posted', 'order.delivered'])
     179    {
     180        Logger::log('SuperFrete', 'Iniciando registro de webhook...');
     181        Logger::log('SuperFrete', "Token sendo usado: " . (empty($this->api_token) ? 'VAZIO' : 'Presente'));
     182        Logger::log('SuperFrete', "URL da API: " . $this->api_url);
     183
     184        // First, check for existing webhooks and clean them up
     185        Logger::log('SuperFrete', 'Verificando webhooks existentes...');
     186        $existing_webhooks = $this->list_webhooks();
     187       
     188        if ($existing_webhooks && is_array($existing_webhooks)) {
     189            Logger::log('SuperFrete', 'Encontrados ' . count($existing_webhooks) . ' webhooks existentes');
     190           
     191            foreach ($existing_webhooks as $webhook) {
     192                if (isset($webhook['id'])) {
     193                    Logger::log('SuperFrete', 'Removendo webhook existente ID: ' . $webhook['id']);
     194                    $this->delete_webhook($webhook['id'], false); // Don't clear options during cleanup
     195                }
     196            }
     197        } else {
     198            Logger::log('SuperFrete', 'Nenhum webhook existente encontrado');
     199        }
     200
     201        // Now register the new webhook
     202        $payload = [
     203            'name' => 'WooCommerce SuperFrete Plugin Webhook',
     204            'url' => $webhook_url,
     205            'events' => $events
    37206        ];
    38      
    39  
    40         $params = [
    41             'headers' => $headers,
    42             'method' => $method,
    43             'timeout' => 10,
     207
     208        Logger::log('SuperFrete', 'Registrando novo webhook: ' . wp_json_encode($payload));
     209
     210        $response = $this->call_superfrete_api('/api/v0/webhook', 'POST', $payload, true);
     211
     212        if ($response && isset($response['secret_token'])) {
     213            // Store webhook secret for signature verification
     214            update_option('superfrete_webhook_secret', $response['secret_token']);
     215            update_option('superfrete_webhook_registered', 'yes');
     216            update_option('superfrete_webhook_url', $webhook_url);
     217            update_option('superfrete_webhook_id', $response['id'] ?? '');
     218           
     219            Logger::log('SuperFrete', 'Webhook registrado com sucesso. ID: ' . ($response['id'] ?? 'N/A'));
     220            return $response;
     221        }
     222
     223        Logger::log('SuperFrete', 'Falha ao registrar webhook: ' . wp_json_encode($response));
     224        update_option('superfrete_webhook_registered', 'no');
     225        return false;
     226    }
     227
     228    /**
     229     * Update existing webhook
     230     */
     231    public function update_webhook($webhook_id, $webhook_url, $events = ['order.posted', 'order.delivered'])
     232    {
     233        $payload = [
     234            'name' => 'WooCommerce SuperFrete Plugin Webhook',
     235            'url' => $webhook_url,
     236            'events' => $events
    44237        ];
    45238
    46         if ($method === 'POST' && !empty($payload)) {
    47             $params['body'] = wp_json_encode($payload);
    48         }
    49  
    50  
    51         $response = ($method === 'POST') ? wp_remote_post($this->api_url . $endpoint, $params) : wp_remote_get($this->api_url . $endpoint, $params);
    52         if(!$retorno){
    53                
    54               if (is_wp_error($response)) {
    55            
    56             Logger::log("Erro na API ({$endpoint}): " . $response->get_error_message(), 'ERROR');
    57             return false;
    58         }
    59      
     239        Logger::log('SuperFrete', 'Atualizando webhook ID: ' . $webhook_id);
     240
     241        $response = $this->call_superfrete_api('/api/v0/webhook/' . $webhook_id, 'PUT', $payload, true);
     242
     243        if ($response) {
     244            update_option('superfrete_webhook_url', $webhook_url);
     245            Logger::log('SuperFrete', 'Webhook atualizado com sucesso');
     246            return $response;
     247        }
     248
     249        Logger::log('SuperFrete', 'Falha ao atualizar webhook: ' . wp_json_encode($response));
     250        return false;
     251    }
     252
     253    /**
     254     * Delete webhook from SuperFrete
     255     */
     256    public function delete_webhook($webhook_id, $clear_options = true)
     257    {
     258        Logger::log('SuperFrete', 'Removendo webhook ID: ' . $webhook_id);
     259
     260        $response = $this->call_superfrete_api('/api/v0/webhook/' . $webhook_id, 'DELETE', [], true);
     261
     262        if ($response !== false) {
     263            if ($clear_options) {
     264                update_option('superfrete_webhook_registered', 'no');
     265                update_option('superfrete_webhook_url', '');
     266                update_option('superfrete_webhook_id', '');
     267            }
     268            Logger::log('SuperFrete', 'Webhook removido com sucesso');
     269            return true;
     270        }
     271
     272        Logger::log('SuperFrete', 'Falha ao remover webhook: ' . wp_json_encode($response));
     273        return false;
     274    }
     275
     276    /**
     277     * List registered webhooks
     278     */
     279    public function list_webhooks()
     280    {
     281        Logger::log('SuperFrete', 'Listando webhooks registrados');
     282       
     283        $response = $this->call_superfrete_api('/api/v0/webhook', 'GET', [], true);
     284       
     285        if ($response) {
     286            Logger::log('SuperFrete', 'Webhooks listados: ' . wp_json_encode($response));
     287            return $response;
     288        }
     289
     290        Logger::log('SuperFrete', 'Falha ao listar webhooks');
     291        return false;
     292    }
    60293}
    61 
    62 
    63      
    64 
    65         $status_code = wp_remote_retrieve_response_code($response);
    66 
    67         $body = json_decode(wp_remote_retrieve_body($response), true);
    68    
    69        
    70         if ($status_code !== 200 || (isset($body['success']) && $body['success'] === false )) {
    71             $error_message = isset($body['message']) ? $body['message'] : 'Erro desconhecido';
    72             $nova_linha = (php_sapi_name() === 'cli') ? "\n" : "<br>";
    73            
    74             if (!isset($body['errors']) && isset($body['error'])) {
    75                 foreach ($body['error'] as $error) {
    76 
    77                     if (isset($erros)) {
    78 
    79                         $erros = $erros . "\n" . $error[0] . "\n";
    80                     } else {
    81                         $erros = "\n" . $error[0];
    82                     }
    83                 }
    84             } else if(isset($body['errors'])){
    85                 foreach ($body['errors'] as $error) {
    86 
    87                     if (isset($erros)) {
    88 
    89                         $erros = $erros . "\n" . $error[0] . "\n";
    90                     } else {
    91                         $erros = "\n" . $error[0];
    92                     }
    93                 }
    94             }
    95 
    96             $errors = isset($erros) ? $erros : 'Sem detalhes';
    97 
    98             Logger::log('SuperFrete', "Erro na API ({$endpoint}): Código {$status_code} - {$error_message}\nDetalhes: {$errors}");
    99             return false;
    100         }
    101 
    102         if (is_wp_error($response)) {
    103 
    104             Logger::log("Erro na API ({$endpoint}): " . $response->get_error_message(), 'ERROR');
    105             return false;
    106         }
    107 
    108         $status_code = wp_remote_retrieve_response_code($response);
    109         if ($status_code !== 200) {
    110             Logger::log("Resposta inesperada ({$endpoint}): HTTP {$status_code}", 'WARNING');
    111             return false;
    112         }
    113 
    114         return json_decode(wp_remote_retrieve_body($response), true);
    115         } catch (Exception $exc) {
    116            Logger::log("Erro {$exc}", 'WARNING');
    117         }
    118 
    119        
    120     }
    121 }
  • superfrete/trunk/app/App.php

    r3289983 r3341621  
    11<?php
    22
    3 namespace SuperFrete;
     3namespace SuperFrete_API;
    44
    55use SuperFrete_API\Helpers\Logger;
     
    2222        add_action('woocommerce_shipping_init', function () {
    2323            if (class_exists('WC_Shipping_Method')) {
     24                require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteBase.php';
    2425                require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFretePAC.php';
    2526                require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteSEDEX.php';
    2627                require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteMiniEnvio.php';
     28                require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteJadlog.php';
     29                require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteLoggi.php';
    2730            }
    2831        });
    2932
    3033        add_filter('woocommerce_shipping_methods', function ($methods) {
    31             if (class_exists('\SuperFrete_API\Shipping\SuperFretePAC') && class_exists('\SuperFrete_API\Shipping\SuperFreteSEDEX') && class_exists('\SuperFrete_API\Shipping\SuperFreteMiniEnvios')) {
    32                 $methods['superfrete_pac'] = '\SuperFrete_API\Shipping\SuperFretePAC';
    33                 $methods['superfrete_sedex'] = '\SuperFrete_API\Shipping\SuperFreteSEDEX';
    34                 $methods['superfrete_mini_envio'] = '\SuperFrete_API\Shipping\SuperFreteMiniEnvios';
    35             }
     34            // Register all individual shipping methods
     35            $methods['superfrete_pac'] = '\SuperFrete_API\Shipping\SuperFretePAC';
     36            $methods['superfrete_sedex'] = '\SuperFrete_API\Shipping\SuperFreteSEDEX';
     37            $methods['superfrete_mini_envio'] = '\SuperFrete_API\Shipping\SuperFreteMiniEnvios';
     38            $methods['superfrete_jadlog'] = '\SuperFrete_API\Shipping\SuperFreteJadlog';
     39            $methods['superfrete_loggi'] = '\SuperFrete_API\Shipping\SuperFreteLoggi';
     40           
     41            // Remove the consolidated method if it exists
     42            unset($methods['superfrete_shipping']);
     43           
    3644            return $methods;
    3745        });
     
    4755        require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/SuperFrete_Settings.php';
    4856        require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/Admin_Menu.php';
     57        require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/WebhookAdmin.php';
    4958        require_once plugin_dir_path(__FILE__) . '../api/Http/Request.php';
     59        require_once plugin_dir_path(__FILE__) . '../api/Http/WebhookVerifier.php';
    5060        require_once plugin_dir_path(__FILE__) . '../api/Helpers/Logger.php';
    5161        require_once plugin_dir_path(__FILE__) . 'Controllers/ProductShipping.php';
    5262        require_once plugin_dir_path(__FILE__) . 'Controllers/SuperFrete_Order.php';
    5363        require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/SuperFrete_OrderActions.php';
    54         require_once plugin_dir_path(__FILE__) . '../app/Helpers/SuperFrete_Notice.php';
     64        require_once plugin_dir_path(__FILE__) . 'Controllers/WebhookController.php';
     65        require_once plugin_dir_path(__FILE__) . 'Controllers/WebhookRetryManager.php';
     66        require_once plugin_dir_path(__FILE__) . 'Controllers/OAuthController.php';
     67        require_once plugin_dir_path(__FILE__) . 'Controllers/DocumentFields.php';
     68        require_once plugin_dir_path(__FILE__) . 'Controllers/CheckoutFields.php';
     69        require_once plugin_dir_path(__FILE__) . 'Helpers/AddressHelper.php';
     70        require_once plugin_dir_path(__FILE__) . 'Helpers/ShippingMigration.php';
     71        require_once plugin_dir_path(__FILE__) . 'Helpers/SuperFrete_Notice.php';
     72       
     73        // Include database migrations if file exists
     74        $migrations_file = plugin_dir_path(__FILE__) . '../database/webhook_migrations.php';
     75        if (file_exists($migrations_file)) {
     76            require_once $migrations_file;
     77        }
    5578    }
    5679
     
    6386        new \SuperFrete_API\Admin\SuperFrete_OrderActions();
    6487        new \SuperFrete_API\Admin\SuperFrete_Settings();
     88        new \SuperFrete_API\Admin\WebhookAdmin();
    6589        new \SuperFrete_API\Controllers\ProductShipping();
    6690        if (class_exists('\SuperFrete_API\Admin\Admin_Menu')) {
     
    6892        }
    6993        new \SuperFrete_API\Controllers\SuperFrete_Order();
     94        new \SuperFrete_API\Controllers\WebhookController();
     95        new \SuperFrete_API\Controllers\WebhookRetryManager();
     96        new \SuperFrete_API\Controllers\OAuthController();
    7097        \SuperFrete_API\Helpers\Logger::init();
     98       
     99        // Initialize webhook database tables (if class exists)
     100        if (class_exists('\SuperFrete_API\Database\WebhookMigrations')) {
     101            \SuperFrete_API\Database\WebhookMigrations::run_migrations();
     102        }
     103       
    71104        add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
    72105        add_action('wp', function () {
     
    75108            }
    76109        });
    77         add_filter('woocommerce_package_rates', [$this, 'ordenar_metodos_frete_por_preco'], 10, 2);
     110        add_filter('woocommerce_package_rates', [$this, 'ordenar_metodos_frete_por_preco'], 100, 2);
     111       
     112        // Also try hooking at an even later stage to ensure our sorting is final
     113        add_filter('woocommerce_shipping_package_rates', [$this, 'ordenar_metodos_frete_por_preco'], 999, 2);
    78114
    79115        // Adiciona os campos 'Número' e 'Bairro' nas configurações da loja
     
    95131        add_action('superfrete_clear_log_event', function () {
    96132            \SuperFrete_API\Helpers\Logger::clear_log();
    97         });
    98         add_action('init', function () {
     133            // Also cleanup old webhook logs (if class exists)
     134            if (class_exists('\SuperFrete_API\Database\WebhookMigrations')) {
     135                \SuperFrete_API\Database\WebhookMigrations::cleanup_old_logs();
     136            }
     137            // Clear old shipping cache entries
     138            if (class_exists('\SuperFrete_API\Shipping\SuperFreteBase')) {
     139                \SuperFrete_API\Shipping\SuperFreteBase::clear_cache();
     140            }
     141        });
     142       
     143        // Register custom order statuses
     144        add_action('init', [$this, 'register_custom_order_statuses']);
     145       
     146        // Run migration and create shipping zone after shipping methods are registered
     147        add_action('wp_loaded', function () {
     148            // Make sure WooCommerce is loaded
     149            if (!class_exists('WooCommerce') || !class_exists('WC_Shipping_Zones')) {
     150                return;
     151            }
     152            // Check if we need to force migration due to plugin update
     153            $current_version = get_option('superfrete_plugin_version', '0.0.0');
     154            $plugin_file = plugin_dir_path(__FILE__) . '../superfrete.php';
     155           
     156            if (file_exists($plugin_file)) {
     157                if (!function_exists('get_plugin_data')) {
     158                    require_once ABSPATH . 'wp-admin/includes/plugin.php';
     159                }
     160                $plugin_data = get_plugin_data($plugin_file);
     161                $new_version = $plugin_data['Version'] ?? '1.0.0';
     162               
     163                // If version changed, reset migration to force re-run
     164                if (version_compare($current_version, $new_version, '<')) {
     165                    delete_option('superfrete_shipping_migrated');
     166                    delete_option('superfrete_individual_methods_migrated'); // New migration flag
     167                    update_option('superfrete_plugin_version', $new_version);
     168                    Logger::log('SuperFrete', "Plugin updated from $current_version to $new_version - forcing migration");
     169                }
     170               
     171                // Force migration for individual methods (version 3.2.0+)
     172                if (version_compare($current_version, '3.2.0', '<') && version_compare($new_version, '3.2.0', '>=')) {
     173                    delete_option('superfrete_shipping_migrated');
     174                    delete_option('superfrete_individual_methods_migrated');
     175                    Logger::log('SuperFrete', "Forcing migration to individual shipping methods (v3.2.0)");
     176                }
     177            }
     178           
     179            // Delay migration to ensure shipping methods are registered
     180            // Only run migration once per request to avoid loops
     181            if (!get_transient('superfrete_migration_running')) {
     182                set_transient('superfrete_migration_running', true, 30); // 30 second lock
     183                \SuperFrete_API\Helpers\ShippingMigration::migrate_shipping_methods();
     184                delete_transient('superfrete_migration_running');
     185            }
    99186            if (!class_exists('\WC_Shipping_Zones')) return;
    100187       
     
    123210                ]);
    124211            }
    125          // Adiciona os métodos: Mini Envios, PAC e SEDEX
    126     $methods_to_add = [
    127         'superfrete_mini_envio',
    128         'superfrete_pac',
    129         'superfrete_sedex',
    130     ];
    131 
    132     foreach ($methods_to_add as $method_id) {
    133         $zone->add_shipping_method($method_id);
    134     }
    135 
    136     // Ativa os métodos
    137     $methods = $zone->get_shipping_methods(true);
    138     foreach ($methods as $method) {
    139         if (in_array($method->id, $methods_to_add)) {
    140             $method->enabled = 'yes';
    141             $method->save();
    142         }
    143     }
     212            // Add all individual SuperFrete methods
     213            $method_ids = ['superfrete_pac', 'superfrete_sedex', 'superfrete_jadlog', 'superfrete_mini_envio', 'superfrete_loggi'];
     214           
     215            // Check if the shipping methods are registered
     216            $wc_shipping = \WC_Shipping::instance();
     217            $available_methods = $wc_shipping->get_shipping_methods();
     218           
     219            foreach ($method_ids as $method_id) {
     220                if (isset($available_methods[$method_id])) {
     221                    $instance_id = $zone->add_shipping_method($method_id);
     222                   
     223                    // Get the method instance and enable it
     224                    $methods = $zone->get_shipping_methods();
     225                    foreach ($methods as $method) {
     226                        if ($method->id === $method_id && $method->get_instance_id() == $instance_id) {
     227                            $method->enabled = 'yes';
     228                            $method->update_option('enabled', 'yes');
     229                            $method->update_option('title', $method->method_title);
     230                            $method->save();
     231                            error_log("✅ SuperFrete shipping method $method_id enabled in zone (Instance ID: $instance_id)");
     232                            break;
     233                        }
     234                    }
     235                } else {
     236                    error_log("❌ SuperFrete shipping method $method_id not registered yet");
     237                }
     238            }
    144239       
    145240            error_log('✅ Zona de entrega "Brasil - SuperFrete" criada com o método ativado.');
     
    152247            return $rates;
    153248
    154         // Reordena os métodos de frete pelo valor (crescente)
     249        // Log original order for debugging
     250        $original_order = [];
     251        foreach ($rates as $rate_id => $rate) {
     252            $original_order[] = $rate->label . ' - R$ ' . number_format(floatval($rate->cost), 2, ',', '.');
     253        }
     254        error_log('SuperFrete: Original shipping order: ' . implode(' | ', $original_order));
     255
     256        // Reordena os métodos de frete pelo valor (crescente - do menor para o maior)
    155257        uasort($rates, function ($a, $b) {
    156             return $a->cost <=> $b->cost;
    157         });
     258            $cost_a = floatval($a->cost);
     259            $cost_b = floatval($b->cost);
     260           
     261            // Free shipping (cost = 0) should come first
     262            if ($cost_a == 0 && $cost_b > 0) return -1;
     263            if ($cost_b == 0 && $cost_a > 0) return 1;
     264           
     265            // Both free or both paid - sort by cost ascending
     266            if ($cost_a == $cost_b) {
     267                // If costs are equal, sort by delivery time (faster first)
     268                $time_a = isset($a->meta_data['delivery_time']) ? intval($a->meta_data['delivery_time']) : 999;
     269                $time_b = isset($b->meta_data['delivery_time']) ? intval($b->meta_data['delivery_time']) : 999;
     270                return $time_a <=> $time_b;
     271            }
     272           
     273            return $cost_a <=> $cost_b;
     274        });
     275
     276        // Log sorted order for debugging
     277        $sorted_order = [];
     278        foreach ($rates as $rate_id => $rate) {
     279            $sorted_order[] = $rate->label . ' - R$ ' . number_format(floatval($rate->cost), 2, ',', '.');
     280        }
     281        error_log('SuperFrete: Sorted shipping order: ' . implode(' | ', $sorted_order));
    158282
    159283        return $rates;
     
    198322    }
    199323
     324    /**
     325     * Register custom order statuses for better tracking
     326     */
     327    public function register_custom_order_statuses()
     328    {
     329        // Register custom 'shipped' status
     330        register_post_status('wc-shipped', [
     331            'label' => 'Enviado',
     332            'public' => true,
     333            'exclude_from_search' => false,
     334            'show_in_admin_all_list' => true,
     335            'show_in_admin_status_list' => true,
     336            'label_count' => _n_noop('Enviado <span class="count">(%s)</span>', 'Enviado <span class="count">(%s)</span>')
     337        ]);
     338       
     339        // Add custom statuses to WooCommerce order statuses
     340        add_filter('wc_order_statuses', function($order_statuses) {
     341            $order_statuses['wc-shipped'] = 'Enviado';
     342            return $order_statuses;
     343        });
     344    }
    200345
    201346    public function superfrete_configs_setup_notice()
     
    265410
    266411        wp_enqueue_style('superfrete-popup-css', plugin_dir_url(__FILE__) . '../assets/styles/superfrete.css', [], '1.0');
    267 
    268         wp_enqueue_style('superfrete-popup-css', plugin_dir_url(__FILE__) . '../assets/styles/superfrete.css', [], '1.0');
     412       
     413        // Add shipping sorting script on checkout
     414        if (is_checkout()) {
     415            add_action('wp_footer', [$this, 'add_shipping_sorting_script']);
     416           
     417            // Enqueue document field script for checkout
     418            wp_enqueue_script(
     419                'superfrete-document-field',
     420                plugin_dir_url(__FILE__) . '../assets/js/document-field.js',
     421                ['jquery'],
     422                '1.0.0',
     423                true
     424            );
     425        }
     426       
     427        // Add theme customization support
     428        add_action('wp_head', [$this, 'add_theme_customization_styles'], 100);
    269429        wp_enqueue_script(
    270430            'superfrete-popup',
     
    340500        wp_send_json_success(['message' => 'Campos vazios preenchidos e endereço atualizado!', 'order_id' => $order_id]);
    341501    }
     502
     503    /**
     504     * Add theme customization styles with CSS variables
     505     */
     506    public function add_theme_customization_styles() {
     507        // Complete CSS variables with SuperFrete brand defaults
     508        $default_variables = array(
     509            // Primary brand colors
     510            '--superfrete-primary-color' => '#0fae79',
     511            '--superfrete-primary-hover' => '#0d9969',
     512            '--superfrete-secondary-color' => '#c3ff01',
     513            '--superfrete-secondary-hover' => '#b3e600',
     514            '--superfrete-success-color' => '#4CAF50',
     515            '--superfrete-error-color' => '#e74c3c',
     516            '--superfrete-info-color' => '#2196F3',
     517            '--superfrete-warning-color' => '#ff9800',
     518           
     519            // Background colors
     520            '--superfrete-bg-color' => '#ffffff',
     521            '--superfrete-bg-white' => '#ffffff',
     522            '--superfrete-bg-light' => '#f8f9fa',
     523            '--superfrete-bg-dark' => '#1a1a1a',
     524           
     525            // Text colors
     526            '--superfrete-text-color' => '#1a1a1a',
     527            '--superfrete-text-light' => '#777777',
     528            '--superfrete-text-white' => '#ffffff',
     529            '--superfrete-heading-color' => '#1a1a1a',
     530           
     531            // Border colors
     532            '--superfrete-border-color' => '#e0e0e0',
     533            '--superfrete-border-light' => '#f0f0f0',
     534            '--superfrete-border-dark' => '#cccccc',
     535           
     536            // Typography
     537            '--superfrete-font-family' => 'Poppins, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
     538            '--superfrete-font-size-small' => '12px',
     539            '--superfrete-font-size-base' => '14px',
     540            '--superfrete-font-size-large' => '16px',
     541            '--superfrete-font-size-xl' => '18px',
     542            '--superfrete-font-weight-normal' => '400',
     543            '--superfrete-font-weight-medium' => '500',
     544            '--superfrete-font-weight-bold' => '600',
     545            '--superfrete-line-height' => '1.5',
     546           
     547            // Spacing
     548            '--superfrete-spacing-xs' => '4px',
     549            '--superfrete-spacing-sm' => '8px',
     550            '--superfrete-spacing-md' => '12px',
     551            '--superfrete-spacing-lg' => '16px',
     552            '--superfrete-spacing-xl' => '24px',
     553            '--superfrete-spacing-xxl' => '32px',
     554           
     555            // Border radius
     556            '--superfrete-radius-sm' => '4px',
     557            '--superfrete-radius-md' => '6px',
     558            '--superfrete-radius-lg' => '8px',
     559            '--superfrete-radius-xl' => '12px',
     560            '--superfrete-radius-full' => '50px',
     561           
     562            // Shadows
     563            '--superfrete-shadow-sm' => '0 1px 3px rgba(0, 0, 0, 0.1)',
     564            '--superfrete-shadow-md' => '0 2px 6px rgba(0, 0, 0, 0.1)',
     565            '--superfrete-shadow-lg' => '0 4px 12px rgba(0, 0, 0, 0.15)',
     566           
     567            // Z-index
     568            '--superfrete-z-base' => '1',
     569            '--superfrete-z-overlay' => '100',
     570            '--superfrete-z-loading' => '101',
     571            '--superfrete-z-modal' => '200',
     572           
     573            // Animation
     574            '--superfrete-transition-fast' => '0.15s ease',
     575            '--superfrete-transition-normal' => '0.3s ease',
     576            '--superfrete-transition-slow' => '0.5s ease',
     577        );
     578
     579        // Get custom CSS variables from database
     580        $custom_variables = get_option('superfrete_custom_css_vars', array());
     581       
     582        // Merge defaults with custom variables
     583        $merged_variables = array_merge($default_variables, $custom_variables);
     584       
     585        // Allow themes to modify CSS variables
     586        $css_variables = apply_filters('superfrete_css_variables', $merged_variables);
     587
     588        // Build CSS string
     589        $custom_css = ':root {';
     590        foreach ($css_variables as $variable => $value) {
     591            $custom_css .= sprintf('%s: %s;', esc_attr($variable), esc_attr($value));
     592        }
     593        $custom_css .= '}';
     594
     595        // Allow themes to add custom CSS
     596        $additional_css = apply_filters('superfrete_custom_css', '');
     597
     598        // Output the styles
     599        if (!empty($custom_css) || !empty($additional_css)) {
     600            echo '<style id="superfrete-theme-customization">';
     601            echo $custom_css;
     602            echo $additional_css;
     603            echo '</style>';
     604        }
     605    }
     606   
     607    /**
     608     * Add JavaScript to sort shipping options by price on the frontend
     609     */
     610    public function add_shipping_sorting_script() {
     611        ?>
     612        <script type="text/javascript">
     613        jQuery(document).ready(function($) {
     614            function sortShippingOptions() {
     615                console.log('SuperFrete: Attempting to sort shipping options...');
     616               
     617                // Try multiple selectors to find shipping options
     618                var $containers = [
     619                    $('#shipping_method'),
     620                    $('.woocommerce-shipping-methods'),
     621                    $('ul.woocommerce-shipping-methods'),
     622                    $('.shipping-methods'),
     623                    $('[id*="shipping_method"]'),
     624                    $('ul li input[name^="shipping_method"]').closest('ul')
     625                ];
     626               
     627                var foundContainer = false;
     628               
     629                $.each($containers, function(index, $container) {
     630                    if ($container.length > 0) {
     631                        console.log('SuperFrete: Found container #' + index + ':', $container);
     632                       
     633                        var $options = $container.find('li');
     634                        if ($options.length <= 1) {
     635                            console.log('SuperFrete: Not enough options to sort (' + $options.length + ')');
     636                            return true; // Continue to next container
     637                        }
     638                       
     639                        foundContainer = true;
     640                        console.log('SuperFrete: Found ' + $options.length + ' shipping options to sort');
     641                       
     642                        // Log original order
     643                        $options.each(function(i) {
     644                            var text = $(this).text();
     645                            var price = extractPrice(text);
     646                            console.log('SuperFrete: Option ' + i + ': ' + text.substring(0, 50) + '... (Price: ' + price + ')');
     647                        });
     648                       
     649                        // Convert to array and sort
     650                        var sortedOptions = $options.get().sort(function(a, b) {
     651                            var priceA = extractPrice($(a).text());
     652                            var priceB = extractPrice($(b).text());
     653                           
     654                            // Free shipping (0) comes first
     655                            if (priceA === 0 && priceB > 0) return -1;
     656                            if (priceB === 0 && priceA > 0) return 1;
     657                           
     658                            // Sort by price ascending
     659                            return priceA - priceB;
     660                        });
     661                       
     662                        // Reorder the DOM elements
     663                        $.each(sortedOptions, function(index, element) {
     664                            $container.append(element);
     665                        });
     666                       
     667                        console.log('SuperFrete: Shipping options sorted successfully!');
     668                       
     669                        // Log sorted order
     670                        $container.find('li').each(function(i) {
     671                            var text = $(this).text();
     672                            var price = extractPrice(text);
     673                            console.log('SuperFrete: Sorted option ' + i + ': ' + text.substring(0, 50) + '... (Price: ' + price + ')');
     674                        });
     675                       
     676                        return false; // Break out of loop
     677                    }
     678                });
     679               
     680                if (!foundContainer) {
     681                    console.log('SuperFrete: No shipping container found. Available elements:');
     682                    console.log('- #shipping_method:', $('#shipping_method').length);
     683                    console.log('- .woocommerce-shipping-methods:', $('.woocommerce-shipping-methods').length);
     684                    console.log('- ul.woocommerce-shipping-methods:', $('ul.woocommerce-shipping-methods').length);
     685                    console.log('- All shipping method inputs:', $('input[name^="shipping_method"]').length);
     686                }
     687            }
     688           
     689            function extractPrice(text) {
     690                // Extract price from text like "R$ 23,72", "R$ 15,49", "Grátis"
     691                if (text.toLowerCase().includes('grátis') || text.toLowerCase().includes('gratuito') || text.toLowerCase().includes('free')) {
     692                    return 0;
     693                }
     694               
     695                // Match pattern like "R$ 23,72" or "23,72"
     696                var match = text.match(/R\$?\s*(\d+)[,.](\d{2})/);
     697                if (match) {
     698                    return parseFloat(match[1] + '.' + match[2]);
     699                }
     700               
     701                // Match pattern like "R$ 23" or "23" 
     702                match = text.match(/R\$?\s*(\d+)/);
     703                if (match) {
     704                    return parseFloat(match[1]);
     705                }
     706               
     707                console.log('SuperFrete: Could not extract price from: ' + text);
     708                return 999999; // Unknown prices go to the end
     709            }
     710           
     711            // Sort on initial load with multiple attempts
     712            console.log('SuperFrete: Initializing shipping sort...');
     713            setTimeout(sortShippingOptions, 500);
     714            setTimeout(sortShippingOptions, 1000);
     715            setTimeout(sortShippingOptions, 2000);
     716            setTimeout(sortShippingOptions, 3000);
     717           
     718            // Sort when shipping is recalculated
     719            $(document.body).on('updated_checkout updated_shipping_method wc_checkout_place_order', function(e) {
     720                console.log('SuperFrete: Checkout updated, re-sorting shipping...', e.type);
     721                setTimeout(sortShippingOptions, 200);
     722                setTimeout(sortShippingOptions, 500);
     723            });
     724           
     725            // Also try when any shipping method is changed
     726            $(document).on('change', 'input[name^="shipping_method"]', function() {
     727                console.log('SuperFrete: Shipping method changed, re-sorting...');
     728                setTimeout(sortShippingOptions, 100);
     729            });
     730        });
     731        </script>
     732        <?php
     733    }
    342734}
  • superfrete/trunk/app/Controllers/Admin/Admin_Menu.php

    r3289983 r3341621  
    11<?php
     2
     3namespace SuperFrete_API\Admin;
    24
    35if (!defined('ABSPATH')) {
     
    57}
    68
    7 /**
    8  * Adiciona submenus ao WooCommerce para exibir logs do SuperFrete e um link para configurações.
    9  */
    10 function superfrete_add_admin_menu() {
    11     add_submenu_page(
    12         'woocommerce', // Adiciona dentro do WooCommerce
    13         'Configurações SuperFrete', // Título da página
    14         'Configurações SuperFrete', // Nome no menu
    15         'manage_woocommerce', // Permissão necessária
    16         'superfrete-settings', // Slug da página
    17         'superfrete_redirect_to_settings' // Callback que redireciona para as configurações
    18     );
     9class Admin_Menu {
     10   
     11    public function __construct() {
     12        add_action('admin_menu', [$this, 'superfrete_add_admin_menu']);
     13    }
    1914
    20     add_submenu_page(
    21         'woocommerce',
    22         'Logs SuperFrete',
    23         'Logs SuperFrete',
    24         'manage_woocommerce',
    25         'superfrete-logs',
    26         'superfrete_display_logs'
    27     );
    28 }
    29 add_action('admin_menu', 'superfrete_add_admin_menu');
     15    /**
     16     * Adiciona submenus ao WooCommerce para exibir logs do SuperFrete e um link para configurações.
     17     */
     18    public function superfrete_add_admin_menu() {
     19        add_submenu_page(
     20            'woocommerce', // Adiciona dentro do WooCommerce
     21            'Configurações SuperFrete', // Título da página
     22            'Configurações SuperFrete', // Nome no menu
     23            'manage_woocommerce', // Permissão necessária
     24            'superfrete-settings', // Slug da página
     25            [$this, 'superfrete_redirect_to_settings'] // Callback que redireciona para as configurações
     26        );
    3027
    31 /**
    32  * Redireciona para a aba de configurações da SuperFrete no WooCommerce.
    33  */
    34 function superfrete_redirect_to_settings() {
    35     wp_redirect(admin_url('admin.php?page=wc-settings&tab=shipping&section=options#superfrete_settings_section-description'));
    36     exit;
    37 }
     28        add_submenu_page(
     29            'woocommerce',
     30            'Logs SuperFrete',
     31            'Logs SuperFrete',
     32            'manage_woocommerce',
     33            'superfrete-logs',
     34            [$this, 'superfrete_display_logs']
     35        );
     36    }
    3837
    39 /**
    40  * Exibe os logs do SuperFrete no painel do WooCommerce.
    41  */
    42 function superfrete_display_logs() {
    43     echo '<div class="wrap">';
    44     echo '<h1>Logs da SuperFrete</h1>';
     38    /**
     39     * Redireciona para a aba de configurações da SuperFrete no WooCommerce.
     40     */
     41    public function superfrete_redirect_to_settings() {
     42        wp_redirect(admin_url('admin.php?page=wc-settings&tab=shipping&section=options#superfrete_settings_section-description'));
     43        exit;
     44    }
    4545
    46     $log_content = SuperFrete_API\Helpers\Logger::get_log();
    47     echo '<textarea readonly style="width:100%; height:500px; font-family:monospace;">';
    48     echo esc_textarea($log_content);
    49     echo '</textarea>';
     46    /**
     47     * Exibe os logs do SuperFrete no painel do WooCommerce.
     48     */
     49    public function superfrete_display_logs() {
     50        echo '<div class="wrap">';
     51        echo '<h1>Logs da SuperFrete</h1>';
    5052
    51     echo '<form method="post" action="">';
    52     wp_nonce_field('clear_log_action'); // Adicionar verificação de nonce
    53     echo '<button type="submit" name="clear_log" class="button button-danger">Limpar Log</button>';
    54     echo '</form>';
     53        $log_content = \SuperFrete_API\Helpers\Logger::get_log();
     54        echo '<textarea readonly style="width:100%; height:500px; font-family:monospace;">';
     55        echo esc_textarea($log_content);
     56        echo '</textarea>';
    5557
    56     echo '</div>';
     58        echo '<form method="post" action="">';
     59        wp_nonce_field('clear_log_action'); // Adicionar verificação de nonce
     60        echo '<button type="submit" name="clear_log" class="button button-danger">Limpar Log</button>';
     61        echo '</form>';
    5762
    58     // Se o botão de limpar log for pressionado
    59     if (isset($_POST['clear_log']) && check_admin_referer('clear_log_action')) {
    60         SuperFrete_API\Helpers\Logger::clear_log();
    61         echo '<script>location.reload();</script>'; // Recarrega a página
     63        echo '</div>';
     64
     65        // Se o botão de limpar log for pressionado
     66        if (isset($_POST['clear_log']) && check_admin_referer('clear_log_action')) {
     67            \SuperFrete_API\Helpers\Logger::clear_log();
     68            echo '<script>location.reload();</script>'; // Recarrega a página
     69        }
    6270    }
    6371}
  • superfrete/trunk/app/Controllers/Admin/SuperFrete_OrderActions.php

    r3289983 r3341621  
    2121        // Adiciona o AJAX para verificar o status da etiqueta
    2222        add_action('wp_ajax_check_superfrete_status', [$this, 'check_superfrete_status']);
     23    }
     24
     25    /**
     26     * Helper method to safely get order meta data (HPOS compatible)
     27     */
     28    private function get_order_meta($order_id, $meta_key, $single = true)
     29    {
     30        $order = wc_get_order($order_id);
     31        if (!$order) {
     32            return $single ? '' : [];
     33        }
     34        return $order->get_meta($meta_key, $single);
     35    }
     36
     37    /**
     38     * Helper method to safely update order meta data (HPOS compatible)
     39     */
     40    private function update_order_meta($order_id, $meta_key, $meta_value)
     41    {
     42        $order = wc_get_order($order_id);
     43        if (!$order) {
     44            return false;
     45        }
     46        $order->update_meta_data($meta_key, $meta_value);
     47        $order->save();
     48        return true;
    2349    }
    2450
     
    114140
    115141        $order_id = intval($_POST['order_id']);
    116         $etiqueta_id = get_post_meta($order_id, '_superfrete_id', true);
     142        $etiqueta_id = $this->get_order_meta($order_id, '_superfrete_id', true);
    117143
    118144        if (!$etiqueta_id) {
     
    126152        $superfrete_tracking = $this->get_superfrete_data($etiqueta_id)['tracking'];
    127153
    128         $valor_frete = floatval(get_post_meta($order_id, '_superfrete_price', true));
     154        $valor_frete = floatval($this->get_order_meta($order_id, '_superfrete_price', true));
    129155
    130156        echo "<p><strong>" . esc_html__('Saldo na SuperFrete:', 'superfrete') . "</strong> R$ " . esc_html(number_format($saldo, 2, ',', '.')) . "</p>";
     
    136162
    137163            echo '<p>' . esc_html__('Status da Etiqueta: ', 'superfrete') . ' <strong>' . esc_html__('Pendente Pagamento', 'superfrete') . '</strong></p>';
    138             $disabled = ($saldo < $valor_frete) ? 'disabled' : '';
    139             echo '<a href="https://web.superfrete.com/#/account/credits" class="button button-primary">' . esc_html__('Adicionar Saldo', 'superfrete') . '</a>';
    140164            if ($saldo < $valor_frete) {
     165                $web_url = $this->get_superfrete_web_url();
     166                echo '<a href="' . esc_url($web_url . '/#/account/credits') . '" target="_blank" class="button button-primary">' . esc_html__('Adicionar Saldo', 'superfrete') . '</a>';
    141167                echo '<p style="color: red;">' . esc_html__('Saldo insuficiente para pagamento da etiqueta.', 'superfrete') . '</p>';
     168            } else {
     169                echo '<a href="' . esc_url(wp_nonce_url(admin_url('admin-post.php?action=superfrete_pay_ticket&order_id=' . $order_id), 'superfrete_pay_ticket')) . '" class="button button-primary">' . esc_html__('Pagar Etiqueta', 'superfrete') . '</a>';
    142170            }
    143171        } else if ($superfrete_status == 'canceled') {
     
    148176        } else if ($superfrete_status == 'posted') {
    149177            echo '<p>' . esc_html__('Status do Pedido: ', 'superfrete') . ' <strong>' . esc_html__('Postado', 'superfrete') . '</strong></p>';
    150             echo '<a target="_blank" href="https://rastreio.superfrete.com/#/tracking/' . $superfrete_tracking . '"  class="button button-primary">' . esc_html__('Rastrear Pedido', 'superfrete') . '</a>';
     178           
     179            // Show webhook tracking info if available
     180            $tracking_code = $this->get_order_meta($order_id, '_superfrete_tracking_code', true);
     181            $tracking_url = $this->get_order_meta($order_id, '_superfrete_tracking_url', true);
     182            $posted_at = $this->get_order_meta($order_id, '_superfrete_posted_at', true);
     183           
     184            if ($tracking_code) {
     185                echo '<p><strong>' . esc_html__('Código de Rastreamento:', 'superfrete') . '</strong> ' . esc_html($tracking_code) . '</p>';
     186            }
     187            if ($posted_at) {
     188                echo '<p><strong>' . esc_html__('Data de Postagem:', 'superfrete') . '</strong> ' . esc_html(wp_date('d/m/Y H:i', strtotime($posted_at))) . '</p>';
     189            }
     190           
     191            $tracking_base_url = $this->get_superfrete_tracking_url();
     192            $tracking_link = $tracking_url ?: "{$tracking_base_url}/#/tracking/{$superfrete_tracking}";
     193            echo '<a href="' . esc_url($tracking_link) . '" target="_blank" class="button button-primary">' . esc_html__('Rastrear Pedido', 'superfrete') . '</a>';
    151194
    152195        } else if ($superfrete_status == 'delivered') {
    153196            echo '<p>' . esc_html__('Status do Pedido: ', 'superfrete') . ' <strong>' . esc_html__('Entregue', 'superfrete') . '</strong></p>';
     197           
     198            // Show delivery info if available
     199            $delivered_at = $this->get_order_meta($order_id, '_superfrete_delivered_at', true);
     200            $tracking_code = $this->get_order_meta($order_id, '_superfrete_tracking_code', true);
     201           
     202            if ($delivered_at) {
     203                echo '<p><strong>' . esc_html__('Data de Entrega:', 'superfrete') . '</strong> ' . esc_html(wp_date('d/m/Y H:i', strtotime($delivered_at))) . '</p>';
     204            }
     205            if ($tracking_code) {
     206                echo '<p><strong>' . esc_html__('Código de Rastreamento:', 'superfrete') . '</strong> ' . esc_html($tracking_code) . '</p>';
     207            }
    154208
    155209        } else {
     
    172226    }
    173227
     228    /**
     229     * Get the correct web URL based on environment
     230     */
     231    private function get_superfrete_web_url()
     232    {
     233        $use_dev_env = get_option('superfrete_sandbox_mode') === 'yes';
     234        return $use_dev_env ? 'https://sandbox.superfrete.com' : 'https://web.superfrete.com';
     235    }
     236
     237    /**
     238     * Get the correct tracking URL based on environment
     239     */
     240    private function get_superfrete_tracking_url()
     241    {
     242        $use_dev_env = get_option('superfrete_sandbox_mode') === 'yes';
     243        return $use_dev_env ? 'https://sandbox.superfrete.com' : 'https://rastreio.superfrete.com';
     244    }
     245
    174246    private function get_superfrete_data($id)
    175247    {
     
    184256    private function get_ticket_superfrete($order_id)
    185257    {
    186         $etiqueta_id = get_post_meta($order_id, '_superfrete_id', true);
     258        $etiqueta_id = $this->get_order_meta($order_id, '_superfrete_id', true);
    187259        if (!$etiqueta_id) {
    188260            return '';
     
    193265
    194266        if (isset($response['url'])) {
    195             update_post_meta($order_id, '_superfrete_status', 'success');
     267            $this->update_order_meta($order_id, '_superfrete_status', 'success');
    196268            return $response['url'];
    197269        }
     
    222294
    223295        if (isset($response['status']) && $response['status'] === 'pending') {
    224             update_post_meta($order_id, '_superfrete_status', 'pending-payment');
     296            $this->update_order_meta($order_id, '_superfrete_status', 'pending-payment');
    225297            Logger::log('SuperFrete', "Pedido #{$order_id} enviado com sucesso.");
    226298        } else {
    227             update_post_meta($order_id, '_superfrete_status', 'erro');
     299            $this->update_order_meta($order_id, '_superfrete_status', 'erro');
    228300            Logger::log('SuperFrete', "Erro ao reenviar pedido #{$order_id}.");
    229301        }
     
    251323        Logger::log('SuperFrete', "Pagando Etiqueta #{$order_id}...");
    252324
    253         $etiqueta_id = get_post_meta($order_id, '_superfrete_id', true);
     325        $etiqueta_id = $this->get_order_meta($order_id, '_superfrete_id', true);
    254326
    255327        $request = new Request();
     
    257329
    258330        if ($response == 409) {
    259             update_post_meta($order_id, '_superfrete_status', 'success');
     331            $this->update_order_meta($order_id, '_superfrete_status', 'success');
    260332            wp_redirect(admin_url('post.php?post=' . $order_id . '&action=edit'));
    261333            return;
     
    264336        if (isset($response['status']) && $response['status'] === 'pending') {
    265337            Logger::log('SuperFrete', "Etiqueta Paga #{$order_id}...");
    266             update_post_meta($order_id, '_superfrete_status', 'pending-payment');
     338            $this->update_order_meta($order_id, '_superfrete_status', 'pending-payment');
    267339            Logger::log('SuperFrete', "Pedido #{$order_id} enviado com sucesso.");
    268340        } else {
    269             update_post_meta($order_id, '_superfrete_status', 'aguardando');
     341            $this->update_order_meta($order_id, '_superfrete_status', 'aguardando');
    270342
    271343            Logger::log('SuperFrete', "Erro ao tentar pagar o ticket do pedido #{$order_id}.");
  • superfrete/trunk/app/Controllers/Admin/SuperFrete_Settings.php

    r3289983 r3341621  
    33
    44use SuperFrete_API\Http\Request;
     5use SuperFrete_API\Helpers\Logger;
    56if (!defined('ABSPATH')) {
    67    exit; // Segurança para evitar acesso direto
     
    3334        }
    3435
    35         if (strlen($token_production) < 1 && isset($legacy_settings['superfrete_token_production'])) {
    36             update_option('superfrete_api_token', $legacy_settings['superfrete_token_production']);
    37         }
    38 
    39         if (strlen($token_sandbox) < 1 && isset($legacy_settings['superfrete_token_sandbox'])) {
    40             update_option('superfrete_api_token_sandbox', $legacy_settings['superfrete_token_sandbox']);
     36        if (strlen($token_production) < 1 && isset($legacy_settings['superfrete_api_token'])) {
     37            update_option('superfrete_api_token', $legacy_settings['superfrete_api_token']);
     38        }
     39
     40        if (strlen($token_sandbox) < 1 && isset($legacy_settings['superfrete_api_token_sandbox'])) {
     41            update_option('superfrete_api_token_sandbox', $legacy_settings['superfrete_api_token_sandbox']);
     42        }
     43
     44        // Auto-register webhooks for existing installations (migration from pre-OAuth to OAuth)
     45        $webhook_migrated = get_option('superfrete_webhook_migrated', 'no');
     46        $webhook_registered = get_option('superfrete_webhook_registered', 'no');
     47       
     48        if ($webhook_migrated !== 'yes' && $webhook_registered !== 'yes') {
     49            // Check if we have existing tokens (indicating this is an existing installation)
     50            $current_sandbox_mode = get_option('superfrete_sandbox_mode', 'no');
     51            $current_token = ($current_sandbox_mode === 'yes') ?
     52                get_option('superfrete_api_token_sandbox') :
     53                get_option('superfrete_api_token');
     54
     55            if (!empty($current_token)) {
     56                Logger::log('SuperFrete', 'Migration: Found existing API token, attempting webhook auto-registration');
     57               
     58                try {
     59                    $request = new Request();
     60                   
     61                    // Validate token first
     62                    $user_response = $request->call_superfrete_api('/api/v0/user', 'GET', [], true);
     63                   
     64                    if ($user_response && isset($user_response['id'])) {
     65                        Logger::log('SuperFrete', 'Migration: Token validated, registering webhook');
     66                       
     67                        // Register webhook
     68                        $webhook_url = rest_url('superfrete/v1/webhook');
     69                        $webhook_result = $request->register_webhook($webhook_url);
     70                       
     71                        if ($webhook_result) {
     72                            update_option('superfrete_webhook_registered', 'yes');
     73                            update_option('superfrete_webhook_url', $webhook_url);
     74                            Logger::log('SuperFrete', 'Migration: Webhook registered successfully: ' . wp_json_encode($webhook_result));
     75                        } else {
     76                            Logger::log('SuperFrete', 'Migration: Webhook registration failed');
     77                        }
     78                    } else {
     79                        Logger::log('SuperFrete', 'Migration: Token validation failed, skipping webhook registration');
     80                    }
     81                } catch (Exception $e) {
     82                    Logger::log('SuperFrete', 'Migration: Webhook registration error: ' . $e->getMessage());
     83                    // Don't break migration for webhook issues
     84                } catch (Error $e) {
     85                    Logger::log('SuperFrete', 'Migration: Webhook registration fatal error: ' . $e->getMessage());
     86                }
     87               
     88                // Mark migration as attempted regardless of success/failure
     89                update_option('superfrete_webhook_migrated', 'yes');
     90            } else {
     91                Logger::log('SuperFrete', 'Migration: No existing token found, skipping webhook auto-registration');
     92                // Mark as migrated since there's nothing to migrate
     93                update_option('superfrete_webhook_migrated', 'yes');
     94            }
    4195        }
    4296    }
     
    47101    public static function add_superfrete_settings($settings) {
    48102       
     103        // Add custom field renderer for webhook status
     104        add_action('woocommerce_admin_field_superfrete_webhook_status', [__CLASS__, 'render_webhook_status_field']);
     105       
     106        // Add custom field renderer for preview
     107        add_action('woocommerce_admin_field_superfrete_preview', [__CLASS__, 'render_preview_field']);
     108       
    49109        $request = new Request();
    50110        $response = $request->call_superfrete_api('/api/v0/user', 'GET', [], true);
    51111       
    52  
     112        $is_connected = ($response && isset($response['id']));
     113        $user_name = $is_connected ? $response['firstname'] . " " . $response['lastname'] : '';
    53114       
    54         $text_conect = ($response)? "Conexão feita para o usuario: ". $response['firstname'] . " " . $response['lastname'] : "Não Conectado";
    55        
     115        // Check webhook status
     116        $webhook_status = self::get_webhook_status();
    56117       
    57118        // Garante que as configurações antigas sejam migradas antes de exibir a página de configurações
     
    65126        ];
    66127
    67        
    68         $settings[] = [
    69     'type'  => 'title',
    70     'title' => $text_conect,
    71     'id'    => 'superfrete_connected_notice'
    72 ];
    73 
    74         $settings[] = [
    75             'title'    => 'Token de Produção',
    76             'desc'     => 'Insira seu token de API para ambiente de produção.<br><br><a target="_blank" href="https://web.superfrete.com/#/integrations" style="display:inline-block; margin-top:15px; padding:6px 12px; background:#0fae79; color:white; text-decoration:none; border-radius:4px;">Gerar Token</a>',
    77             'id'       => 'superfrete_api_token',
    78             'type'     => 'text',
    79             'css'      => 'width: 400px;',
    80             'desc_tip' => 'Token de autenticação para API em ambiente de produção.',
    81             'default'  => get_option('superfrete_api_token', ''),
    82         ];
     128        if ($is_connected) {
     129            // Show connected status and reconnect option
     130            $settings[] = [
     131                'type'  => 'title',
     132                'title' => '✅ Conectado como: ' . $user_name,
     133                'id'    => 'superfrete_connected_notice'
     134            ];
     135
     136            $settings[] = [
     137                'title'    => 'Gerenciar Conexão',
     138                'desc'     => 'Sua conta SuperFrete está conectada e funcionando.<br><br>' .
     139                             '<button type="button" id="superfrete-oauth-btn" class="button" style="margin-top:15px; padding:6px 12px; background:#f0ad4e; color:white; text-decoration:none; border-radius:4px; border:none; cursor:pointer;" onclick="return confirm(\'Tem certeza que deseja reconectar? Isso irá substituir a conexão atual.\');">' .
     140                             'Reconectar Integração' .
     141                             '</button>' .
     142                             '<div id="superfrete-oauth-status" style="margin-top:10px;"></div>',
     143                'id'       => 'superfrete_oauth_reconnection',
     144                'type'     => 'title',
     145                'desc_tip' => 'Use apenas se houver problemas com a conexão atual.',
     146            ];
     147        } else {
     148            // Show connection setup
     149            $settings[] = [
     150                'type'  => 'title',
     151                'title' => '❌ Não Conectado',
     152                'id'    => 'superfrete_disconnected_notice'
     153            ];
     154
     155            $settings[] = [
     156                'title'    => 'Conexão SuperFrete',
     157                'desc'     => 'Conecte sua conta SuperFrete automaticamente via OAuth.<br><br>' .
     158                             '<button type="button" id="superfrete-oauth-btn" class="button button-primary" style="margin-top:15px; padding:6px 12px; background:#0fae79; color:white; text-decoration:none; border-radius:4px; border:none; cursor:pointer;">' .
     159                             'Conectar com SuperFrete' .
     160                             '</button>' .
     161                             '<div id="superfrete-oauth-status" style="margin-top:10px;"></div>',
     162                'id'       => 'superfrete_oauth_connection',
     163                'type'     => 'title',
     164                'desc_tip' => 'Use o botão acima para conectar sua conta SuperFrete de forma segura.',
     165            ];
     166        }
    83167   
    84 $settings[] = [
     168        $settings[] = [
     169            'title'    => 'Ativar Calculadora',
     170            'desc'     => 'Habilitar calculadora de frete na página do produto',
     171            'id'       => 'superfrete_enable_calculator',
     172            'type'     => 'checkbox',
     173            'default'  => 'yes',
     174            'desc_tip' => 'Ativar a calculadora de frete na página do produto.',
     175        ];
     176
     177        $settings[] = [
     178            'title'    => 'Cálculo Automático',
     179            'desc'     => 'Calcular frete automaticamente ao carregar a página do produto',
     180            'id'       => 'superfrete_auto_calculation',
     181            'type'     => 'checkbox',
     182            'default'  => 'no', // Disabled by default for better performance
     183            'desc_tip' => 'Quando desabilitado, o frete só será calculado quando o usuário clicar no botão. Recomendado para melhor performance.',
     184        ];
     185
     186
     187
     188        $settings[] = [
     189            'type' => 'sectionend',
     190            'id'   => 'superfrete_settings_section'
     191        ];
     192
     193        // Visual Customization Section
     194        $settings[] = [
     195            'title' => 'Personalização Visual',
     196            'type'  => 'title',
     197            'desc'  => 'Personalize as cores e aparência da calculadora de frete.<br><br>' .
     198                      '<div style="margin: 15px 0; padding: 15px; background: #f9f9f9; border-radius: 5px;">' .
     199                      '<strong>Presets de Tema:</strong><br>' .
     200                      '<button type="button" id="superfrete-preset-light" class="button" style="margin: 5px 10px 5px 0;">🌞 Tema Claro</button>' .
     201                      '<button type="button" id="superfrete-preset-dark" class="button" style="margin: 5px 10px 5px 0;">🌙 Tema Escuro</button>' .
     202                      '<button type="button" id="superfrete-preset-auto" class="button" style="margin: 5px 0;">🎨 Auto-Detectar</button>' .
     203                      '</div>',
     204            'id'    => 'superfrete_visual_section'
     205        ];
     206
     207        $settings[] = [
     208            'title'    => 'Pré-visualização',
     209            'desc'     => 'Veja como a calculadora ficará com suas personalizações',
     210            'id'       => 'superfrete_preview',
     211            'type'     => 'superfrete_preview',
     212        ];
     213
     214        $settings[] = [
     215            'title'    => 'Cor Principal',
     216            'desc'     => 'Cor principal dos botões, preços e elementos interativos',
     217            'id'       => 'superfrete_custom_primary_color',
     218            'type'     => 'color',
     219            'default'  => '#0fae79',
     220            'css'      => 'width: 6em;',
     221            'desc_tip' => 'Usada para botões, preços, bordas de foco e elementos de destaque.',
     222        ];
     223
     224        $settings[] = [
     225            'title'    => 'Cor de Erro',
     226            'desc'     => 'Cor para mensagens de erro e alertas',
     227            'id'       => 'superfrete_custom_error_color',
     228            'type'     => 'color',
     229            'default'  => '#e74c3c',
     230            'css'      => 'width: 6em;',
     231            'desc_tip' => 'Usada para indicar erros e alertas.',
     232        ];
     233
     234        $settings[] = [
     235            'title'    => 'Fundo da Calculadora',
     236            'desc'     => 'Cor de fundo principal da calculadora',
     237            'id'       => 'superfrete_custom_bg_color',
     238            'type'     => 'color',
     239            'default'  => '#ffffff',
     240            'css'      => 'width: 6em;',
     241            'desc_tip' => 'Cor de fundo do container principal da calculadora.',
     242        ];
     243
     244        $settings[] = [
     245            'title'    => 'Fundo dos Resultados',
     246            'desc'     => 'Cor de fundo da área de resultados',
     247            'id'       => 'superfrete_custom_results_bg_color',
     248            'type'     => 'color',
     249            'default'  => '#ffffff',
     250            'css'      => 'width: 6em;',
     251            'desc_tip' => 'Cor de fundo onde são exibidos os métodos de envio.',
     252        ];
     253
     254        $settings[] = [
     255            'title'    => 'Cor do Texto Principal',
     256            'desc'     => 'Cor do texto principal e títulos',
     257            'id'       => 'superfrete_custom_text_color',
     258            'type'     => 'color',
     259            'default'  => '#1a1a1a',
     260            'css'      => 'width: 6em;',
     261            'desc_tip' => 'Cor do texto principal, títulos e labels.',
     262        ];
     263
     264        $settings[] = [
     265            'title'    => 'Cor do Texto Secundário',
     266            'desc'     => 'Cor do texto secundário e placeholders',
     267            'id'       => 'superfrete_custom_text_light_color',
     268            'type'     => 'color',
     269            'default'  => '#777777',
     270            'css'      => 'width: 6em;',
     271            'desc_tip' => 'Cor do texto secundário, placeholders e descrições.',
     272        ];
     273
     274        $settings[] = [
     275            'title'    => 'Cor das Bordas',
     276            'desc'     => 'Cor das bordas dos elementos',
     277            'id'       => 'superfrete_custom_border_color',
     278            'type'     => 'color',
     279            'default'  => '#e0e0e0',
     280            'css'      => 'width: 6em;',
     281            'desc_tip' => 'Cor das bordas dos campos de input e containers.',
     282        ];
     283
     284        $settings[] = [
     285            'title'    => 'Tamanho da Fonte',
     286            'desc'     => 'Tamanho base da fonte na calculadora',
     287            'id'       => 'superfrete_custom_font_size',
     288            'type'     => 'select',
     289            'default'  => '14px',
     290            'options'  => [
     291                '12px' => 'Pequeno (12px)',
     292                '14px' => 'Médio (14px)',
     293                '16px' => 'Grande (16px)',
     294                '18px' => 'Muito Grande (18px)',
     295            ],
     296            'desc_tip' => 'Ajuste o tamanho da fonte para melhor legibilidade.',
     297        ];
     298
     299        $settings[] = [
     300            'title'    => 'Bordas Arredondadas',
     301            'desc'     => 'Nível de arredondamento das bordas',
     302            'id'       => 'superfrete_custom_border_radius',
     303            'type'     => 'select',
     304            'default'  => '4px',
     305            'options'  => [
     306                '0px' => 'Sem Arredondamento',
     307                '2px' => 'Pouco Arredondado',
     308                '4px' => 'Médio',
     309                '8px' => 'Muito Arredondado',
     310                '12px' => 'Extremamente Arredondado',
     311            ],
     312            'desc_tip' => 'Ajuste o estilo das bordas dos elementos.',
     313        ];
     314
     315        $settings[] = [
     316            'title'    => 'Resetar Personalização',
     317            'desc'     => 'Voltar às configurações visuais padrão do SuperFrete',
     318            'id'       => 'superfrete_reset_customization',
     319            'type'     => 'button',
     320            'desc_tip' => 'Clique para restaurar todas as configurações visuais para os valores padrão.',
     321            'custom'   => '<button type="button" id="superfrete-reset-customization" class="button button-secondary">Resetar para Padrão</button>',
     322        ];
     323
     324        $settings[] = [
     325            'type' => 'sectionend',
     326            'id'   => 'superfrete_visual_section'
     327        ];
     328
     329        // Advanced Configuration Section (Accordion)
     330        $settings[] = [
     331            'title' => 'Configurações Avançadas',
     332            'type'  => 'title',
     333            'desc'  => '<div id="superfrete-advanced-toggle" style="cursor: pointer; padding: 10px; background: #f1f1f1; border: 1px solid #ddd; border-radius: 4px; margin: 10px 0;">' .
     334                      '<span style="font-weight: bold;">▼ Mostrar Configurações Avançadas</span>' .
     335                      '</div>',
     336            'id'    => 'superfrete_advanced_section'
     337        ];
     338
     339        $settings[] = [
    85340            'title'    => 'Ativar Sandbox',
    86341            'desc'     => 'Habilitar ambiente de testes',
     
    88343            'type'     => 'checkbox',
    89344            'default'  => get_option('superfrete_sandbox_mode', 'no'),
    90             'desc_tip' => 'Ao ativar o modo sandbox, a API usará um token de teste.'
    91         ];
    92            
    93 $settings[] = [
    94     'title'    => 'Ativar Calculadora',
    95     'desc'     => 'Habilitar calculadora de frete na página do produto',
    96     'id'       => 'superfrete_enable_calculator',
    97     'type'     => 'checkbox',
    98     'default'  => 'yes',
    99     'desc_tip' => 'Ativar a calculadora de frete na página do produto.',
    100 ];
    101 
    102         $settings[] = [
    103             'title'    => 'Token de Sandbox',
    104             'desc'     => 'Insira seu token de API para o ambiente de teste.',
    105             'id'       => 'superfrete_api_token_sandbox',
    106             'type'     => 'text',
    107             'css'      => 'width: 400px;',
    108             'desc_tip' => 'Token de autenticação para API no ambiente de testes (sandbox).',
    109             'class'    => 'superfrete-sandbox-field',
    110             'default'  => get_option('superfrete_api_token_sandbox', ''),
     345            'desc_tip' => 'Ao ativar o modo sandbox, a API usará o ambiente de testes da SuperFrete.'
     346        ];
     347
     348        $settings[] = [
     349            'type'  => 'superfrete_webhook_status',
     350            'title' => 'Status dos Webhooks',
     351            'id'    => 'superfrete_webhook_status',
     352            'webhook_status' => $webhook_status,
     353            'class' => 'superfrete-advanced-field'
    111354        ];
    112355
    113356        $settings[] = [
    114357            'type' => 'sectionend',
    115             'id'   => 'superfrete_settings_section'
     358            'id'   => 'superfrete_advanced_section'
    116359        ];
    117360
     
    123366     */
    124367    public static function enqueue_admin_scripts() {
    125         $current_screen = get_current_screen();
    126         if ($current_screen->id === 'woocommerce_page_wc-settings') {
    127             ?>
    128             <script>
     368        // Check if we're on a WooCommerce settings page
     369        if (self::is_woocommerce_settings_page()) {
     370            // Ensure jQuery is loaded
     371            wp_enqueue_script('jquery');
     372           
     373            // Enqueue calculator CSS for preview
     374            wp_enqueue_style('superfrete-calculator-css', plugin_dir_url(__FILE__) . '../../../assets/styles/superfrete-calculator.css', [], '1.0');
     375           
     376            // Add inline script
     377            add_action('admin_footer', function() {
     378                ?>
     379                <script type="text/javascript">
    129380                jQuery(document).ready(function($) {
    130                     function toggleSandboxField() {
    131                         if ($('#superfrete_sandbox_mode').is(':checked')) {
    132                             $('.superfrete-sandbox-field').closest('tr').show();
     381                    console.log('SuperFrete admin scripts loading...'); // Debug log
     382                   
     383                    // Make sure ajaxurl is available
     384                    if (typeof ajaxurl === 'undefined') {
     385                        window.ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
     386                    }
     387                   
     388                    // Advanced settings accordion toggle
     389                    $('#superfrete-advanced-toggle').on('click', function() {
     390                        // Target specific field IDs for advanced settings
     391                        var $sandboxField = $('#superfrete_sandbox_mode').closest('tr');
     392                        var $webhookField = $('.superfrete-advanced-field').closest('tr');
     393                        var $advancedFields = $sandboxField.add($webhookField);
     394                        var $toggle = $(this).find('span');
     395                       
     396                        if ($advancedFields.is(':visible')) {
     397                            $advancedFields.slideUp();
     398                            $toggle.text('▼ Mostrar Configurações Avançadas');
    133399                        } else {
    134                             $('.superfrete-sandbox-field').closest('tr').hide();
     400                            $advancedFields.slideDown();
     401                            $toggle.text('▲ Ocultar Configurações Avançadas');
    135402                        }
     403                    });
     404                   
     405                    // Initially hide advanced fields
     406                    // $('#superfrete_sandbox_mode').closest('tr').hide(); // Show sandbox mode by default
     407                    $('.superfrete-advanced-field').closest('tr').hide();
     408                   
     409                    // Add OAuth functionality
     410                    $('#superfrete-oauth-btn').on('click', function() {
     411                        console.log('SuperFrete OAuth button clicked');
     412                       
     413                        var $button = $(this);
     414                        var $status = $('#superfrete-oauth-status');
     415                       
     416                        // Disable button and show loading
     417                        var originalText = $button.text();
     418                        var isReconnecting = originalText.includes('Reconectar');
     419                        $button.prop('disabled', true).text(isReconnecting ? 'Reconectando...' : 'Conectando...');
     420                        $status.html('<span style="color: #0073aa;">Iniciando ' + (isReconnecting ? 'reconexão' : 'conexão') + ' com SuperFrete...</span>');
     421                       
     422                        // Get API URL based on environment settings
     423                        <?php
     424                        $use_dev_env = get_option('superfrete_sandbox_mode', 'no') === 'yes';
     425                        $api_url = $use_dev_env ? 'https://api.dev.superintegrador.superfrete.com' : 'https://api.superintegrador.superfrete.com';
     426                        ?>
     427                       
     428                        var apiUrl = '<?php echo $api_url; ?>';
     429                        var siteUrl = '<?php echo get_site_url(); ?>';
     430                        var oauthUrl = apiUrl + '/headless/oauth/init?callback_url=' + encodeURIComponent(siteUrl + '/wp-admin/admin-ajax.php?action=superfrete_oauth_callback');
     431                       
     432                        // Open OAuth popup
     433                        var popup = window.open(
     434                            oauthUrl,
     435                            'superfrete_oauth',
     436                            'width=600,height=700,scrollbars=yes,resizable=yes'
     437                        );
     438                       
     439                        // Extract session ID from the OAuth URL for polling
     440                        var sessionId = null;
     441                       
     442                        // Listen for session ID from popup
     443                        var sessionListener = function(event) {
     444                            if (event.origin !== apiUrl) {
     445                                return;
     446                            }
     447                           
     448                            if (event.data.type === 'superfrete_session_id') {
     449                                sessionId = event.data.session_id;
     450                                console.log('Received session ID:', sessionId);
     451                                // Start polling now that we have the session ID
     452                                setTimeout(pollForToken, 2000);
     453                            }
     454                        };
     455                       
     456                        window.addEventListener('message', sessionListener);
     457                       
     458                        // Poll for token completion
     459                        var pollForToken = function() {
     460                            if (!sessionId) {
     461                                console.log('No session ID yet, waiting...');
     462                                return;
     463                            }
     464                           
     465                            // Poll the WordPress proxy for token (bypasses CORS)
     466                            // Try REST API first, fallback to AJAX if needed
     467                            var restUrl = '<?php echo rest_url('superfrete/v1/oauth/token'); ?>?session_id=' + sessionId;
     468                            var ajaxUrl = ajaxurl + '?action=superfrete_oauth_proxy&session_id=' + sessionId + '&nonce=' + encodeURIComponent('<?php echo wp_create_nonce('wp_rest'); ?>');
     469                           
     470                            $.ajax({
     471                                url: restUrl,
     472                                type: 'GET',
     473                                beforeSend: function(xhr) {
     474                                    xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
     475                                },
     476                                success: function(response) {
     477                                    if (response.ready && response.access_token) {
     478                                        // Token is ready - validate and save it
     479                                        $status.html('<span style="color: #0073aa;">Validando token...</span>');
     480                                       
     481                                        // Send token to WordPress for validation and storage
     482                                        $.ajax({
     483                                            url: ajaxurl,
     484                                            type: 'POST',
     485                                            data: {
     486                                                action: 'superfrete_oauth_callback',
     487                                                token: response.access_token,
     488                                                session_id: sessionId,
     489                                                nonce: '<?php echo wp_create_nonce('superfrete_oauth_nonce'); ?>'
     490                                            },
     491                                            success: function(wpResponse) {
     492                                                if (wpResponse.success) {
     493                                                    oauthSuccessful = true; // Mark OAuth as successful
     494                                                    $status.html('<span style="color: #46b450;">✓ ' + wpResponse.data.message + '</span>');
     495                                                    $button.prop('disabled', false).text('Reconectar');
     496                                                   
     497                                                    // Show success message with user info
     498                                                    var userInfo = wpResponse.data.user_info;
     499                                                    var successMsg = 'SuperFrete conectado com sucesso!';
     500                                                    if (userInfo && userInfo.name) {
     501                                                        successMsg += '<br>Usuário: ' + userInfo.name;
     502                                                        if (userInfo.email) {
     503                                                            successMsg += ' (' + userInfo.email + ')';
     504                                                        }
     505                                                        if (userInfo.balance !== undefined) {
     506                                                            successMsg += '<br>Saldo: R$ ' + parseFloat(userInfo.balance).toFixed(2);
     507                                                        }
     508                                                    }
     509                                                    if (wpResponse.data.webhook_registered) {
     510                                                        successMsg += '<br>✅ Webhooks registrados automaticamente';
     511                                                    }
     512                                                   
     513                                                    $('<div class="notice notice-success is-dismissible"><p>' + successMsg + '</p></div>')
     514                                                        .insertAfter('.wrap h1');
     515                                                       
     516                                                    // Refresh the page to update connection status
     517                                                    setTimeout(function() {
     518                                                        location.reload();
     519                                                    }, 3000);
     520                                                } else {
     521                                                    $status.html('<span style="color: #dc3232;">✗ Erro: ' + wpResponse.data + '</span>');
     522                                                    $button.prop('disabled', false).text('Tentar Novamente');
     523                                                }
     524                                            },
     525                                            error: function(xhr, status, error) {
     526                                                $status.html('<span style="color: #dc3232;">✗ Erro de comunicação: ' + error + '</span>');
     527                                                $button.prop('disabled', false).text('Tentar Novamente');
     528                                            }
     529                                        });
     530                                       
     531                                        // Close popup
     532                                        popup.close();
     533                                    } else if (!response.ready) {
     534                                        // Token not ready yet, continue polling
     535                                        setTimeout(pollForToken, 2000);
     536                                    }
     537                                },
     538                                error: function(xhr, status, error) {
     539                                    if (xhr.status === 404 && xhr.responseJSON && xhr.responseJSON.message === 'Not Found') {
     540                                        // REST API endpoint not found, try AJAX fallback
     541                                        console.log('REST API failed, trying AJAX fallback...');
     542                                        $.ajax({
     543                                            url: ajaxUrl,
     544                                            type: 'GET',
     545                                            success: function(response) {
     546                                                // Handle the same way as REST API response
     547                                                if (response.ready && response.access_token) {
     548                                                    // Process token the same way
     549                                                    $status.html('<span style="color: #0073aa;">Validando token...</span>');
     550                                                   
     551                                                    // Send token to WordPress for validation and storage
     552                                                    $.ajax({
     553                                                        url: ajaxurl,
     554                                                        type: 'POST',
     555                                                        data: {
     556                                                            action: 'superfrete_oauth_callback',
     557                                                            token: response.access_token,
     558                                                            session_id: sessionId,
     559                                                            nonce: '<?php echo wp_create_nonce('superfrete_oauth_nonce'); ?>'
     560                                                        },
     561                                                        success: function(wpResponse) {
     562                                                            if (wpResponse.success) {
     563                                                                oauthSuccessful = true; // Mark OAuth as successful
     564                                                                $status.html('<span style="color: #46b450;">✓ ' + wpResponse.data.message + '</span>');
     565                                                                $button.prop('disabled', false).text('Reconectar');
     566                                                               
     567                                                                var userInfo = wpResponse.data.user_info;
     568                                                                var successMsg = 'SuperFrete conectado com sucesso!';
     569                                                                if (userInfo && userInfo.name) {
     570                                                                    successMsg += '<br>Usuário: ' + userInfo.name;
     571                                                                    if (userInfo.email) {
     572                                                                        successMsg += ' (' + userInfo.email + ')';
     573                                                                    }
     574                                                                    if (userInfo.balance !== undefined) {
     575                                                                        successMsg += '<br>Saldo: R$ ' + parseFloat(userInfo.balance).toFixed(2);
     576                                                                    }
     577                                                                }
     578                                                                if (wpResponse.data.webhook_registered) {
     579                                                                    successMsg += '<br>✅ Webhooks registrados automaticamente';
     580                                                                }
     581                                                               
     582                                                                $('<div class="notice notice-success is-dismissible"><p>' + successMsg + '</p></div>')
     583                                                                    .insertAfter('.wrap h1');
     584                                                                   
     585                                                                setTimeout(function() {
     586                                                                    location.reload();
     587                                                                }, 3000);
     588                                                            } else {
     589                                                                $status.html('<span style="color: #dc3232;">✗ Erro: ' + wpResponse.data + '</span>');
     590                                                                $button.prop('disabled', false).text('Tentar Novamente');
     591                                                            }
     592                                                        },
     593                                                        error: function(xhr, status, error) {
     594                                                            $status.html('<span style="color: #dc3232;">✗ Erro de comunicação: ' + error + '</span>');
     595                                                            $button.prop('disabled', false).text('Tentar Novamente');
     596                                                        }
     597                                                    });
     598                                                   
     599                                                    popup.close();
     600                                                } else if (!response.ready) {
     601                                                    // Token not ready yet, continue polling with AJAX
     602                                                    setTimeout(function() {
     603                                                        // Switch to AJAX polling
     604                                                        pollForTokenAjax();
     605                                                    }, 2000);
     606                                                }
     607                                            },
     608                                            error: function(xhr, status, error) {
     609                                                if (xhr.status === 404) {
     610                                                    // Session not found or expired
     611                                                    $status.html('<span style="color: #dc3232;">✗ Sessão expirada. Tente novamente.</span>');
     612                                                    $button.prop('disabled', false).text('Tentar Novamente');
     613                                                    popup.close();
     614                                                } else {
     615                                                    // Continue polling on other errors
     616                                                    setTimeout(pollForToken, 2000);
     617                                                }
     618                                            }
     619                                        });
     620                                    } else if (xhr.status === 404) {
     621                                        // Session not found or expired
     622                                        $status.html('<span style="color: #dc3232;">✗ Sessão expirada. Tente novamente.</span>');
     623                                        $button.prop('disabled', false).text('Tentar Novamente');
     624                                        popup.close();
     625                                    } else {
     626                                        // Continue polling on other errors
     627                                        setTimeout(pollForToken, 2000);
     628                                    }
     629                                }
     630                            });
     631                        };
     632                       
     633                        // AJAX polling function (fallback)
     634                        var pollForTokenAjax = function() {
     635                            if (!sessionId) {
     636                                console.log('No session ID yet, waiting...');
     637                                return;
     638                            }
     639                           
     640                            $.ajax({
     641                                url: ajaxUrl,
     642                                type: 'GET',
     643                                success: function(response) {
     644                                    if (response.ready && response.access_token) {
     645                                        // Token is ready - validate and save it
     646                                        $status.html('<span style="color: #0073aa;">Validando token...</span>');
     647                                       
     648                                        // Send token to WordPress for validation and storage
     649                                        $.ajax({
     650                                            url: ajaxurl,
     651                                            type: 'POST',
     652                                            data: {
     653                                                action: 'superfrete_oauth_callback',
     654                                                token: response.access_token,
     655                                                session_id: sessionId,
     656                                                nonce: '<?php echo wp_create_nonce('superfrete_oauth_nonce'); ?>'
     657                                            },
     658                                            success: function(wpResponse) {
     659                                                if (wpResponse.success) {
     660                                                    oauthSuccessful = true; // Mark OAuth as successful
     661                                                    $status.html('<span style="color: #46b450;">✓ ' + wpResponse.data.message + '</span>');
     662                                                    $button.prop('disabled', false).text('Reconectar');
     663                                                   
     664                                                    var userInfo = wpResponse.data.user_info;
     665                                                    var successMsg = 'SuperFrete conectado com sucesso!';
     666                                                    if (userInfo && userInfo.name) {
     667                                                        successMsg += '<br>Usuário: ' + userInfo.name;
     668                                                        if (userInfo.email) {
     669                                                            successMsg += ' (' + userInfo.email + ')';
     670                                                        }
     671                                                        if (userInfo.balance !== undefined) {
     672                                                            successMsg += '<br>Saldo: R$ ' + parseFloat(userInfo.balance).toFixed(2);
     673                                                        }
     674                                                    }
     675                                                    if (wpResponse.data.webhook_registered) {
     676                                                        successMsg += '<br>✅ Webhooks registrados automaticamente';
     677                                                    }
     678                                                   
     679                                                    $('<div class="notice notice-success is-dismissible"><p>' + successMsg + '</p></div>')
     680                                                        .insertAfter('.wrap h1');
     681                                                       
     682                                                    setTimeout(function() {
     683                                                        location.reload();
     684                                                    }, 3000);
     685                                                } else {
     686                                                    $status.html('<span style="color: #dc3232;">✗ Erro: ' + wpResponse.data + '</span>');
     687                                                    $button.prop('disabled', false).text('Tentar Novamente');
     688                                                }
     689                                            },
     690                                            error: function(xhr, status, error) {
     691                                                $status.html('<span style="color: #dc3232;">✗ Erro de comunicação: ' + error + '</span>');
     692                                                $button.prop('disabled', false).text('Tentar Novamente');
     693                                            }
     694                                        });
     695                                       
     696                                        popup.close();
     697                                    } else if (!response.ready) {
     698                                        // Token not ready yet, continue polling
     699                                        setTimeout(pollForTokenAjax, 2000);
     700                                    }
     701                                },
     702                                error: function(xhr, status, error) {
     703                                    if (xhr.status === 404) {
     704                                        // Session not found or expired
     705                                        $status.html('<span style="color: #dc3232;">✗ Sessão expirada. Tente novamente.</span>');
     706                                        $button.prop('disabled', false).text('Tentar Novamente');
     707                                        popup.close();
     708                                    } else {
     709                                        // Continue polling on other errors
     710                                        setTimeout(pollForTokenAjax, 2000);
     711                                    }
     712                                }
     713                            });
     714                        };
     715                       
     716                        // Handle popup closed manually
     717                        var pollingActive = true;
     718                        var oauthSuccessful = false; // Track if OAuth was successful
     719                        var manualClose = false; // Track if popup was closed manually
     720                        var checkClosed = setInterval(function() {
     721                            if (popup.closed) {
     722                                clearInterval(checkClosed);
     723                                pollingActive = false;
     724                                window.removeEventListener('message', sessionListener);
     725                               
     726                                // Only show "Conexão cancelada" if the popup was closed manually
     727                                // and OAuth wasn't successful
     728                                if (manualClose && !oauthSuccessful) {
     729                                    $button.prop('disabled', false).text(originalText);
     730                                    $status.html('<span style="color: #dc3232;">Conexão cancelada</span>');
     731                                }
     732                            }
     733                        }, 1000);
     734                       
     735                        // Track manual popup close
     736                        popup.onbeforeunload = function() {
     737                            manualClose = true;
     738                        };
     739                       
     740                        // Update polling function to check if still active
     741                        var originalPollForToken = pollForToken;
     742                        pollForToken = function() {
     743                            if (!pollingActive) {
     744                                return;
     745                            }
     746                            originalPollForToken();
     747                        };
     748                    });
     749                   
     750                    // Handle visual customization settings
     751                    function updateCSSVariables() {
     752                        var primaryColor = $('#superfrete_custom_primary_color').val();
     753                        var errorColor = $('#superfrete_custom_error_color').val();
     754                        var fontSize = $('#superfrete_custom_font_size').val();
     755                        var borderRadius = $('#superfrete_custom_border_radius').val();
     756                       
     757                        // Background and text colors
     758                        var bgColor = $('#superfrete_custom_bg_color').val();
     759                        var resultsBgColor = $('#superfrete_custom_results_bg_color').val();
     760                        var textColor = $('#superfrete_custom_text_color').val();
     761                        var textLightColor = $('#superfrete_custom_text_light_color').val();
     762                        var borderColor = $('#superfrete_custom_border_color').val();
     763                       
     764                        // Update preview immediately
     765                        updatePreview(primaryColor, errorColor, fontSize, borderRadius, bgColor, resultsBgColor, textColor, textLightColor, borderColor);
     766                       
     767                        // Save to database via AJAX
     768                        $.ajax({
     769                            url: ajaxurl,
     770                            type: 'POST',
     771                            data: {
     772                                action: 'superfrete_save_customization',
     773                                nonce: '<?php echo wp_create_nonce('superfrete_customization_nonce'); ?>',
     774                                primary_color: primaryColor,
     775                                error_color: errorColor,
     776                                font_size: fontSize,
     777                                border_radius: borderRadius,
     778                                bg_color: bgColor,
     779                                results_bg_color: resultsBgColor,
     780                                text_color: textColor,
     781                                text_light_color: textLightColor,
     782                                border_color: borderColor
     783                            },
     784                            success: function(response) {
     785                                if (response.success) {
     786                                    // Silently saved - no notification needed for real-time updates
     787                                    console.log('Customization saved successfully');
     788                                } else {
     789                                    console.error('Error saving customization:', response.data);
     790                                }
     791                            },
     792                            error: function(xhr, status, error) {
     793                                console.error('AJAX error:', error);
     794                            }
     795                        });
    136796                    }
    137 
    138                     // Executa ao carregar a página
    139                     toggleSandboxField();
    140 
    141                     // Monitora mudanças no checkbox
    142                     $('#superfrete_sandbox_mode').change(function() {
    143                         toggleSandboxField();
     797                   
     798                    // Function to update the preview in real-time
     799                    function updatePreview(primaryColor, errorColor, fontSize, borderRadius, bgColor, resultsBgColor, textColor, textLightColor, borderColor) {
     800                        // Helper function to darken color
     801                        function darkenColor(color, percent) {
     802                            var num = parseInt(color.replace('#', ''), 16),
     803                                amt = Math.round(2.55 * percent),
     804                                R = (num >> 16) - amt,
     805                                G = (num >> 8 & 0x00FF) - amt,
     806                                B = (num & 0x0000FF) - amt;
     807                            return '#' + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
     808                                (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
     809                                (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
     810                        }
     811                       
     812                        // Helper function to lighten color
     813                        function lightenColor(color, percent) {
     814                            var num = parseInt(color.replace('#', ''), 16),
     815                                amt = Math.round(2.55 * percent),
     816                                R = (num >> 16) + amt,
     817                                G = (num >> 8 & 0x00FF) + amt,
     818                                B = (num & 0x0000FF) + amt;
     819                            return '#' + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
     820                                (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
     821                                (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
     822                        }
     823                       
     824                        // Helper function to scale size
     825                        function scaleSize(size, multiplier) {
     826                            var numericValue = parseFloat(size);
     827                            var unit = size.replace(numericValue, '');
     828                            return (numericValue * multiplier) + unit;
     829                        }
     830                       
     831                        // Generate comprehensive CSS variables with all necessary variables
     832                        var css = '#super-frete-shipping-calculator-preview {' +
     833                            // Primary colors
     834                            '--superfrete-primary-color: ' + primaryColor + ';' +
     835                            '--superfrete-primary-hover: ' + darkenColor(primaryColor, 10) + ';' +
     836                            '--superfrete-error-color: ' + errorColor + ';' +
     837                           
     838                            // Background colors
     839                            '--superfrete-bg-color: ' + bgColor + ';' +
     840                            '--superfrete-bg-white: ' + resultsBgColor + ';' +
     841                            '--superfrete-bg-light: ' + lightenColor(bgColor, 5) + ';' +
     842                           
     843                            // Text colors
     844                            '--superfrete-text-color: ' + textColor + ';' +
     845                            '--superfrete-text-light: ' + textLightColor + ';' +
     846                            '--superfrete-heading-color: ' + textColor + ';' +
     847                           
     848                            // Border colors
     849                            '--superfrete-border-color: ' + borderColor + ';' +
     850                            '--superfrete-border-light: ' + lightenColor(borderColor, 10) + ';' +
     851                            '--superfrete-border-dark: ' + darkenColor(borderColor, 15) + ';' +
     852                           
     853                            // Typography
     854                            '--superfrete-font-size-base: ' + fontSize + ';' +
     855                            '--superfrete-font-size-small: ' + scaleSize(fontSize, 0.85) + ';' +
     856                            '--superfrete-font-size-large: ' + scaleSize(fontSize, 1.15) + ';' +
     857                            '--superfrete-font-family: Poppins, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;' +
     858                            '--superfrete-line-height: 1.5;' +
     859                           
     860                            // Border radius
     861                            '--superfrete-radius-sm: ' + borderRadius + ';' +
     862                            '--superfrete-radius-md: ' + scaleSize(borderRadius, 1.5) + ';' +
     863                            '--superfrete-radius-lg: ' + scaleSize(borderRadius, 2) + ';' +
     864                           
     865                            // Spacing (essential for layout)
     866                            '--superfrete-spacing-xs: 4px;' +
     867                            '--superfrete-spacing-sm: 8px;' +
     868                            '--superfrete-spacing-md: 12px;' +
     869                            '--superfrete-spacing-lg: 16px;' +
     870                            '--superfrete-spacing-xl: 24px;' +
     871                           
     872                            // Shadows
     873                            '--superfrete-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);' +
     874                            '--superfrete-shadow-md: 0 2px 6px rgba(0, 0, 0, 0.1);' +
     875                           
     876                            // Z-index
     877                            '--superfrete-z-base: 1;' +
     878                            '--superfrete-z-overlay: 100;' +
     879                            '--superfrete-z-loading: 101;' +
     880                           
     881                            // Animation
     882                            '--superfrete-transition-fast: 0.15s ease;' +
     883                            '--superfrete-transition-normal: 0.3s ease;' +
     884                        '}';
     885                       
     886                        // Update the preview styles
     887                        $('#superfrete-preview-styles').html(css);
     888                    }
     889                   
     890                    // Debounce function to prevent excessive updates
     891                    function debounce(func, wait) {
     892                        let timeout;
     893                        return function executedFunction(...args) {
     894                            const later = () => {
     895                                clearTimeout(timeout);
     896                                func(...args);
     897                            };
     898                            clearTimeout(timeout);
     899                            timeout = setTimeout(later, wait);
     900                        };
     901                    }
     902                   
     903                    // Create debounced update function
     904                    const debouncedUpdateCSS = debounce(function() {
     905                        // Save current scroll position
     906                        const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
     907                        const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
     908                       
     909                        // Update CSS
     910                        updateCSSVariables();
     911                       
     912                        // Restore scroll position
     913                        window.scrollTo(scrollLeft, scrollTop);
     914                    }, 300);
     915                   
     916                    // Auto-save on color picker changes
     917                    $('#superfrete_custom_primary_color, #superfrete_custom_error_color, #superfrete_custom_bg_color, #superfrete_custom_results_bg_color, #superfrete_custom_text_color, #superfrete_custom_text_light_color, #superfrete_custom_border_color').on('change', function() {
     918                        updateCSSVariables();
    144919                    });
     920                   
     921                    // Disable WordPress Iris color picker and use HTML5 color inputs
     922                    $('input[type="color"]').each(function() {
     923                        var $this = $(this);
     924                        // Remove any WordPress color picker initialization
     925                        if ($this.hasClass('wp-color-picker')) {
     926                            $this.wpColorPicker('destroy');
     927                        }
     928                        // Remove wp-color-picker class to prevent auto-initialization
     929                        $this.removeClass('wp-color-picker');
     930                    });
     931                   
     932                    // Auto-save on dropdown changes
     933                    $('#superfrete_custom_font_size, #superfrete_custom_border_radius').on('change', function() {
     934                        updateCSSVariables();
     935                    });
     936                   
     937                    // Theme preset handlers
     938                    $('#superfrete-preset-light').on('click', function() {
     939                        // Light theme preset
     940                        $('#superfrete_custom_bg_color').val('#ffffff').trigger('change');
     941                        $('#superfrete_custom_results_bg_color').val('#ffffff').trigger('change');
     942                        $('#superfrete_custom_text_color').val('#1a1a1a').trigger('change');
     943                        $('#superfrete_custom_text_light_color').val('#777777').trigger('change');
     944                        $('#superfrete_custom_border_color').val('#e0e0e0').trigger('change');
     945                       
     946                        // Force update of color picker UI
     947                        setTimeout(function() {
     948                            updateCSSVariables();
     949                        }, 100);
     950                    });
     951                   
     952                    $('#superfrete-preset-dark').on('click', function() {
     953                        // Dark theme preset
     954                        $('#superfrete_custom_bg_color').val('#2a2a2a').trigger('change');
     955                        $('#superfrete_custom_results_bg_color').val('#333333').trigger('change');
     956                        $('#superfrete_custom_text_color').val('#ffffff').trigger('change');
     957                        $('#superfrete_custom_text_light_color').val('#cccccc').trigger('change');
     958                        $('#superfrete_custom_border_color').val('#555555').trigger('change');
     959                       
     960                        // Force update of color picker UI
     961                        setTimeout(function() {
     962                            updateCSSVariables();
     963                        }, 100);
     964                    });
     965                   
     966                    $('#superfrete-preset-auto').on('click', function() {
     967                        // Auto-detect theme from page background
     968                        var bodyBg = $('body').css('background-color');
     969                        var isLightTheme = true;
     970                       
     971                        // Simple light/dark detection
     972                        if (bodyBg && bodyBg !== 'rgba(0, 0, 0, 0)') {
     973                            var rgb = bodyBg.match(/\\d+/g);
     974                            if (rgb && rgb.length >= 3) {
     975                                var brightness = (parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000;
     976                                isLightTheme = brightness > 128;
     977                            }
     978                        }
     979                       
     980                        if (isLightTheme) {
     981                            $('#superfrete-preset-light').click();
     982                        } else {
     983                            $('#superfrete-preset-dark').click();
     984                        }
     985                    });
     986                   
     987                    // Handle reset customization
     988                    $('#superfrete-reset-customization').on('click', function() {
     989                        if (confirm('Tem certeza que deseja resetar todas as personalizações visuais?')) {
     990                            $.ajax({
     991                                url: ajaxurl,
     992                                type: 'POST',
     993                                data: {
     994                                    action: 'superfrete_reset_customization',
     995                                    nonce: '<?php echo wp_create_nonce('superfrete_customization_nonce'); ?>'
     996                                },
     997                                success: function(response) {
     998                                    if (response.success) {
     999                                        // Reset form values to defaults and trigger change events
     1000                                        $('#superfrete_custom_primary_color').val('#0fae79').trigger('change');
     1001                                        $('#superfrete_custom_error_color').val('#e74c3c').trigger('change');
     1002                                        $('#superfrete_custom_font_size').val('14px').trigger('change');
     1003                                        $('#superfrete_custom_border_radius').val('4px').trigger('change');
     1004                                        $('#superfrete_custom_bg_color').val('#ffffff').trigger('change');
     1005                                        $('#superfrete_custom_results_bg_color').val('#ffffff').trigger('change');
     1006                                        $('#superfrete_custom_text_color').val('#1a1a1a').trigger('change');
     1007                                        $('#superfrete_custom_text_light_color').val('#777777').trigger('change');
     1008                                        $('#superfrete_custom_border_color').val('#e0e0e0').trigger('change');
     1009                                       
     1010                                        // Update preview with default values
     1011                                        updatePreview('#0fae79', '#e74c3c', '14px', '4px', '#ffffff', '#ffffff', '#1a1a1a', '#777777', '#e0e0e0');
     1012                                       
     1013                                        // Show success notification
     1014                                        $('<div class="notice notice-success is-dismissible"><p>Personalização resetada com sucesso!</p></div>')
     1015                                            .insertAfter('.wrap h1').delay(3000).fadeOut();
     1016                                    } else {
     1017                                        console.error('Error resetting customization:', response.data);
     1018                                    }
     1019                                },
     1020                                error: function(xhr, status, error) {
     1021                                    console.error('AJAX error:', error);
     1022                                }
     1023                            });
     1024                        }
     1025                    });
     1026                   
     1027                    // Initialize preview with current values
     1028                    function initializePreview() {
     1029                        var primaryColor = $('#superfrete_custom_primary_color').val() || '#0fae79';
     1030                        var errorColor = $('#superfrete_custom_error_color').val() || '#e74c3c';
     1031                        var fontSize = $('#superfrete_custom_font_size').val() || '14px';
     1032                        var borderRadius = $('#superfrete_custom_border_radius').val() || '4px';
     1033                        var bgColor = $('#superfrete_custom_bg_color').val() || '#ffffff';
     1034                        var resultsBgColor = $('#superfrete_custom_results_bg_color').val() || '#ffffff';
     1035                        var textColor = $('#superfrete_custom_text_color').val() || '#1a1a1a';
     1036                        var textLightColor = $('#superfrete_custom_text_light_color').val() || '#777777';
     1037                        var borderColor = $('#superfrete_custom_border_color').val() || '#e0e0e0';
     1038                       
     1039                        updatePreview(primaryColor, errorColor, fontSize, borderRadius, bgColor, resultsBgColor, textColor, textLightColor, borderColor);
     1040                    }
     1041                   
     1042                    // Initialize preview on page load
     1043                    initializePreview();
     1044                   
     1045                    console.log('SuperFrete admin scripts loaded successfully!');
    1451046                });
    146             </script>
    147             <?php
    148         }
     1047                </script>
     1048                <?php
     1049            });
     1050        }
     1051    }
     1052   
     1053    /**
     1054     * Check if we're on a WooCommerce settings page
     1055     */
     1056    private static function is_woocommerce_settings_page() {
     1057        global $pagenow;
     1058       
     1059        // Check URL parameters
     1060        $page = $_GET['page'] ?? '';
     1061        $tab = $_GET['tab'] ?? '';
     1062       
     1063        return (
     1064            $pagenow === 'admin.php' &&
     1065            $page === 'wc-settings' &&
     1066            ($tab === 'shipping' || $tab === '')
     1067        );
     1068    }
     1069
     1070    /**
     1071     * Render custom webhook status field
     1072     */
     1073    public static function render_webhook_status_field($field) {
     1074        $webhook_status = $field['webhook_status'];
     1075        $css_class = isset($field['class']) ? $field['class'] : '';
     1076        ?>
     1077        <tr valign="top" class="<?php echo esc_attr($css_class); ?>">
     1078            <th scope="row" class="titledesc">
     1079                <label for="<?php echo esc_attr($field['id']); ?>"><?php echo esc_html($field['title']); ?></label>
     1080            </th>
     1081            <td class="forminp">
     1082                <div style="<?php echo esc_attr($webhook_status['css']); ?>">
     1083                    <?php if (strpos($webhook_status['message'], 'Registrados e Ativos') !== false): ?>
     1084                        ✅ Webhooks Registrados e Ativos
     1085                        <button type="button" id="superfrete-register-webhook" class="button button-secondary" style="margin-left: 10px;">
     1086                            Reregistrar
     1087                        </button>
     1088                    <?php else: ?>
     1089                        ❌ Webhooks Não Registrados
     1090                        <button type="button" id="superfrete-register-webhook" class="button button-primary" style="margin-left: 10px;">
     1091                            Registrar Agora
     1092                        </button>
     1093                    <?php endif; ?>
     1094                </div>
     1095                <p class="description">Status atual dos webhooks do SuperFrete.</p>
     1096            </td>
     1097        </tr>
     1098        <?php
     1099    }
     1100
     1101    /**
     1102     * Render custom preview field
     1103     */
     1104    public static function render_preview_field($field) {
     1105        ?>
     1106        <tr valign="top">
     1107            <th scope="row" class="titledesc">
     1108                <label for="<?php echo esc_attr($field['id']); ?>"><?php echo esc_html($field['title']); ?></label>
     1109            </th>
     1110            <td class="forminp">
     1111                <?php echo self::render_preview_html(); ?>
     1112                <p class="description"><?php echo esc_html($field['desc']); ?></p>
     1113            </td>
     1114        </tr>
     1115        <?php
     1116    }
     1117
     1118    /**
     1119     * Get webhook status information
     1120     */
     1121    public static function get_webhook_status() {
     1122        $is_registered = get_option('superfrete_webhook_registered') === 'yes';
     1123        $webhook_url = get_option('superfrete_webhook_url');
     1124       
     1125        if ($is_registered && $webhook_url) {
     1126            return [
     1127                'message' => 'Webhooks Registrados e Ativos',
     1128                'css' => 'color: #008000; font-weight: bold;'
     1129            ];
     1130        } else {
     1131            return [
     1132                'message' => 'Webhooks Não Registrados',
     1133                'css' => 'color: #cc0000; font-weight: bold;'
     1134            ];
     1135        }
     1136    }
     1137
     1138    /**
     1139     * Handle webhook registration AJAX request
     1140     */
     1141    public static function handle_webhook_registration() {
     1142        // Log the request for debugging
     1143        Logger::log('SuperFrete', 'Webhook registration AJAX request received');
     1144       
     1145        // Verify nonce
     1146        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'superfrete_webhook_nonce')) {
     1147            Logger::log('SuperFrete', 'Webhook registration failed: Invalid nonce');
     1148            wp_send_json_error('Falha na verificação de segurança');
     1149            return;
     1150        }
     1151
     1152        // Check permissions
     1153        if (!current_user_can('manage_woocommerce')) {
     1154            Logger::log('SuperFrete', 'Webhook registration failed: Insufficient permissions');
     1155            wp_send_json_error('Permissões insuficientes');
     1156            return;
     1157        }
     1158
     1159        try {
     1160            $webhook_url = rest_url('superfrete/v1/webhook');
     1161            Logger::log('SuperFrete', 'Attempting webhook registration for URL: ' . $webhook_url);
     1162           
     1163            $request = new Request();
     1164           
     1165            // Try to register webhook
     1166            $result = $request->register_webhook($webhook_url);
     1167           
     1168            if ($result) {
     1169                Logger::log('SuperFrete', 'Webhook registration successful: ' . wp_json_encode($result));
     1170                wp_send_json_success('Webhook registrado com sucesso!');
     1171            } else {
     1172                Logger::log('SuperFrete', 'Webhook registration failed: No result returned');
     1173                wp_send_json_error('Falha ao registrar webhook. Verifique suas credenciais da API e conexão.');
     1174            }
     1175        } catch (Exception $e) {
     1176            Logger::log('SuperFrete', 'Webhook registration exception: ' . $e->getMessage());
     1177            wp_send_json_error('Erro: ' . $e->getMessage());
     1178        } catch (Error $e) {
     1179            Logger::log('SuperFrete', 'Webhook registration error: ' . $e->getMessage());
     1180            wp_send_json_error('Erro interno: ' . $e->getMessage());
     1181        }
     1182    }
     1183
     1184    /**
     1185     * Handle OAuth callback AJAX request
     1186     */
     1187    public static function handle_oauth_callback() {
     1188        // Verify nonce for security
     1189        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'superfrete_oauth_nonce')) {
     1190            wp_send_json_error('Falha na verificação de segurança');
     1191            return;
     1192        }
     1193
     1194        // Check permissions
     1195        if (!current_user_can('manage_woocommerce')) {
     1196            wp_send_json_error('Permissões insuficientes');
     1197            return;
     1198        }
     1199
     1200        // Get the token from the request
     1201        $token = sanitize_text_field($_POST['token'] ?? '');
     1202        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
     1203
     1204        if (empty($token)) {
     1205            wp_send_json_error('Token não fornecido');
     1206            return;
     1207        }
     1208
     1209        try {
     1210            // Determine which environment we're using
     1211            $sandbox_enabled = get_option('superfrete_sandbox_mode') === 'yes';
     1212            $site_url = get_site_url();
     1213            $is_dev_site = (
     1214                strpos($site_url, 'localhost') !== false ||
     1215                strpos($site_url, '.local') !== false ||
     1216                (
     1217                    strpos($site_url, 'dev.') !== false &&
     1218                    strpos($site_url, 'dev.wordpress.superintegrador.superfrete.com') === false
     1219                )
     1220            );
     1221            $use_dev_env = ($sandbox_enabled || $is_dev_site);
     1222            $option_key = $use_dev_env ? 'superfrete_api_token_sandbox' : 'superfrete_api_token';
     1223            $old_token = get_option($option_key);
     1224           
     1225            // Temporarily set the token to test it
     1226            update_option($option_key, $token);
     1227           
     1228            // Validate token using superfrete-api package
     1229            $request = new Request();
     1230           
     1231            // Add detailed logging before API call
     1232            Logger::log('SuperFrete', 'About to validate token with API call to /api/v0/user');
     1233            Logger::log('SuperFrete', 'Token being validated: ' . substr($token, 0, 20) . '...');
     1234            Logger::log('SuperFrete', 'Environment: ' . ($use_dev_env ? 'dev' : 'production'));
     1235            Logger::log('SuperFrete', 'Option key: ' . $option_key);
     1236           
     1237            $response = $request->call_superfrete_api('/api/v0/user', 'GET', [], true);
     1238           
     1239            // Log the response
     1240            Logger::log('SuperFrete', 'API response received: ' . wp_json_encode($response));
     1241            Logger::log('SuperFrete', 'Response has id: ' . (isset($response['id']) ? 'yes' : 'no'));
     1242            Logger::log('SuperFrete', 'Response is truthy: ' . ($response ? 'yes' : 'no'));
     1243           
     1244            if ($response && isset($response['id'])) {
     1245                // Token is valid, keep it
     1246                Logger::log('SuperFrete', 'OAuth token validated successfully for user: ' . ($response['firstname'] ?? 'Unknown'));
     1247               
     1248                // Register webhooks automatically after successful token validation
     1249                try {
     1250                    $webhook_url = rest_url('superfrete/v1/webhook');
     1251                    $webhook_result = $request->register_webhook($webhook_url);
     1252                   
     1253                    if ($webhook_result) {
     1254                        Logger::log('SuperFrete', 'Webhook registered automatically after OAuth: ' . wp_json_encode($webhook_result));
     1255                        update_option('superfrete_webhook_registered', 'yes');
     1256                        update_option('superfrete_webhook_url', $webhook_url);
     1257                    } else {
     1258                        Logger::log('SuperFrete', 'Webhook registration failed after OAuth');
     1259                    }
     1260                } catch (Exception $webhook_error) {
     1261                    Logger::log('SuperFrete', 'Webhook registration error after OAuth: ' . $webhook_error->getMessage());
     1262                    // Don't fail the OAuth process if webhook registration fails
     1263                }
     1264               
     1265                wp_send_json_success([
     1266                    'message' => 'Token OAuth obtido e validado com sucesso!',
     1267                    'user_info' => [
     1268                        'name' => ($response['firstname'] ?? '') . ' ' . ($response['lastname'] ?? ''),
     1269                        'email' => $response['email'] ?? '',
     1270                        'id' => $response['id'] ?? '',
     1271                        'balance' => $response['balance'] ?? 0,
     1272                        'limits' => $response['limits'] ?? []
     1273                    ],
     1274                    'webhook_registered' => isset($webhook_result) && $webhook_result ? true : false
     1275                ]);
     1276            } else {
     1277                // Token is invalid, restore old token
     1278                update_option($option_key, $old_token);
     1279                Logger::log('SuperFrete', 'OAuth token validation failed');
     1280                wp_send_json_error('Token inválido ou expirado');
     1281            }
     1282        } catch (Exception $e) {
     1283            // Restore old token on error
     1284            if (isset($old_token)) {
     1285                update_option($option_key, $old_token);
     1286            }
     1287            Logger::log('SuperFrete', 'OAuth token validation error: ' . $e->getMessage());
     1288            wp_send_json_error('Erro ao validar token: ' . $e->getMessage());
     1289        }
     1290    }
     1291
     1292    /**
     1293     * Handle save customization AJAX request
     1294     */
     1295    public static function handle_save_customization() {
     1296        // Verify nonce
     1297        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'superfrete_customization_nonce')) {
     1298            wp_send_json_error('Falha na verificação de segurança');
     1299            return;
     1300        }
     1301
     1302        // Check permissions
     1303        if (!current_user_can('manage_woocommerce')) {
     1304            wp_send_json_error('Permissões insuficientes');
     1305            return;
     1306        }
     1307
     1308        try {
     1309            // Get and sanitize input values
     1310            $primary_color = sanitize_hex_color($_POST['primary_color'] ?? '#0fae79');
     1311            $error_color = sanitize_hex_color($_POST['error_color'] ?? '#e74c3c');
     1312            $font_size = sanitize_text_field($_POST['font_size'] ?? '14px');
     1313            $border_radius = sanitize_text_field($_POST['border_radius'] ?? '4px');
     1314           
     1315            // Background and text colors
     1316            $bg_color = sanitize_hex_color($_POST['bg_color'] ?? '#ffffff');
     1317            $results_bg_color = sanitize_hex_color($_POST['results_bg_color'] ?? '#ffffff');
     1318            $text_color = sanitize_hex_color($_POST['text_color'] ?? '#1a1a1a');
     1319            $text_light_color = sanitize_hex_color($_POST['text_light_color'] ?? '#666666');
     1320            $border_color = sanitize_hex_color($_POST['border_color'] ?? '#e0e0e0');
     1321
     1322            // Validate values
     1323            $valid_font_sizes = ['12px', '14px', '16px', '18px'];
     1324            $valid_border_radius = ['0px', '2px', '4px', '8px', '12px'];
     1325           
     1326            if (!in_array($font_size, $valid_font_sizes)) {
     1327                $font_size = '14px';
     1328            }
     1329            if (!in_array($border_radius, $valid_border_radius)) {
     1330                $border_radius = '4px';
     1331            }
     1332
     1333            // Build comprehensive CSS variables array
     1334            $css_variables = array(
     1335                // Primary colors and variations
     1336                '--superfrete-primary-color' => $primary_color,
     1337                '--superfrete-primary-hover' => self::darken_color($primary_color, 10),
     1338                '--superfrete-error-color' => $error_color,
     1339               
     1340                // Background colors
     1341                '--superfrete-bg-color' => $bg_color,
     1342                '--superfrete-bg-white' => $results_bg_color,
     1343                '--superfrete-bg-light' => self::lighten_color($bg_color, 5),
     1344               
     1345                // Text colors
     1346                '--superfrete-text-color' => $text_color,
     1347                '--superfrete-text-light' => $text_light_color,
     1348                '--superfrete-heading-color' => $text_color,
     1349               
     1350                // Border colors
     1351                '--superfrete-border-color' => $border_color,
     1352                '--superfrete-border-light' => self::lighten_color($border_color, 10),
     1353                '--superfrete-border-dark' => self::darken_color($border_color, 15),
     1354               
     1355                // Interactive element colors (use primary color)
     1356                '--superfrete-interactive-color' => $primary_color,
     1357                '--superfrete-interactive-hover' => self::darken_color($primary_color, 10),
     1358               
     1359                // Typography
     1360                '--superfrete-font-size-base' => $font_size,
     1361                '--superfrete-font-size-small' => self::scale_size($font_size, 0.85),
     1362                '--superfrete-font-size-large' => self::scale_size($font_size, 1.15),
     1363               
     1364                // Border radius
     1365                '--superfrete-radius-sm' => $border_radius,
     1366                '--superfrete-radius-md' => self::scale_size($border_radius, 1.5),
     1367                '--superfrete-radius-lg' => self::scale_size($border_radius, 2),
     1368               
     1369                // Derived colors based on primary color for better theming
     1370                '--superfrete-focus-color' => $primary_color,
     1371                '--superfrete-accent-color' => $primary_color,
     1372            );
     1373
     1374            // Save to database
     1375            update_option('superfrete_custom_css_vars', $css_variables);
     1376
     1377            wp_send_json_success('Personalização salva com sucesso!');
     1378        } catch (Exception $e) {
     1379            wp_send_json_error('Erro ao salvar personalização: ' . $e->getMessage());
     1380        }
     1381    }
     1382
     1383    /**
     1384     * Handle reset customization AJAX request
     1385     */
     1386    public static function handle_reset_customization() {
     1387        // Verify nonce
     1388        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'superfrete_customization_nonce')) {
     1389            wp_send_json_error('Falha na verificação de segurança');
     1390            return;
     1391        }
     1392
     1393        // Check permissions
     1394        if (!current_user_can('manage_woocommerce')) {
     1395            wp_send_json_error('Permissões insuficientes');
     1396            return;
     1397        }
     1398
     1399        try {
     1400            // Remove custom CSS variables from database
     1401            delete_option('superfrete_custom_css_vars');
     1402
     1403            wp_send_json_success('Personalização resetada com sucesso!');
     1404        } catch (Exception $e) {
     1405            wp_send_json_error('Erro ao resetar personalização: ' . $e->getMessage());
     1406        }
     1407    }
     1408
     1409    /**
     1410     * Helper function to darken a color
     1411     */
     1412    private static function darken_color($color, $percent) {
     1413        // Remove # if present
     1414        $color = str_replace('#', '', $color);
     1415       
     1416        // Convert to RGB
     1417        $r = hexdec(substr($color, 0, 2));
     1418        $g = hexdec(substr($color, 2, 2));
     1419        $b = hexdec(substr($color, 4, 2));
     1420       
     1421        // Darken by percent
     1422        $r = max(0, min(255, $r - ($r * $percent / 100)));
     1423        $g = max(0, min(255, $g - ($g * $percent / 100)));
     1424        $b = max(0, min(255, $b - ($b * $percent / 100)));
     1425       
     1426        // Convert back to hex
     1427        return sprintf('#%02x%02x%02x', $r, $g, $b);
     1428    }
     1429
     1430    /**
     1431     * Helper function to scale a size value
     1432     */
     1433    private static function scale_size($size, $multiplier) {
     1434        $numeric_value = floatval($size);
     1435        $unit = str_replace($numeric_value, '', $size);
     1436        return ($numeric_value * $multiplier) . $unit;
     1437    }
     1438
     1439    /**
     1440     * Helper function to lighten a color
     1441     */
     1442    private static function lighten_color($color, $percent) {
     1443        // Remove # if present
     1444        $color = str_replace('#', '', $color);
     1445       
     1446        // Convert to RGB
     1447        $r = hexdec(substr($color, 0, 2));
     1448        $g = hexdec(substr($color, 2, 2));
     1449        $b = hexdec(substr($color, 4, 2));
     1450       
     1451        // Lighten by percent
     1452        $r = min(255, max(0, $r + (255 - $r) * $percent / 100));
     1453        $g = min(255, max(0, $g + (255 - $g) * $percent / 100));
     1454        $b = min(255, max(0, $b + (255 - $b) * $percent / 100));
     1455       
     1456        // Convert back to hex
     1457        return sprintf('#%02x%02x%02x', $r, $g, $b);
     1458    }
     1459
     1460    /**
     1461     * Render preview HTML for the freight calculator
     1462     */
     1463    private static function render_preview_html() {
     1464        ob_start();
     1465        ?>
     1466        <div id="superfrete-preview-container" style="margin-top: 20px; padding: 20px; background: #f9f9f9; border-radius: 8px;">
     1467            <h4 style="margin-top: 0;">Pré-visualização da Calculadora</h4>
     1468            <div id="superfrete-preview-calculator" style="max-width: 500px;">
     1469                <!-- Exact replica of the real calculator structure -->
     1470                <div id="super-frete-shipping-calculator-preview" class="superfrete-calculator-wrapper">
     1471                    <!-- CEP Input Section - Always Visible -->
     1472                    <div class="superfrete-input-section">
     1473                        <div class="form-row form-row-wide" id="calc_shipping_postcode_field">
     1474                            <input type="text" class="input-text" value="22775-360"
     1475                                   placeholder="Digite seu CEP (00000-000)"
     1476                                   readonly style="pointer-events: none;" />
     1477                        </div>
     1478                    </div>
     1479
     1480                    <!-- Results Section -->
     1481                    <div id="superfrete-results-container-preview" class="superfrete-results-container">
     1482                        <div class="superfrete-shipping-methods">
     1483                            <h3>Opções de Entrega</h3>
     1484                            <div class="superfrete-shipping-method">
     1485                                <div class="superfrete-shipping-method-name">PAC - (5 dias úteis)</div>
     1486                                <div class="superfrete-shipping-method-price">R$ 20,78</div>
     1487                            </div>
     1488                            <div class="superfrete-shipping-method">
     1489                                <div class="superfrete-shipping-method-name">SEDEX - (1 dia útil)</div>
     1490                                <div class="superfrete-shipping-method-price">R$ 13,20</div>
     1491                            </div>
     1492                        </div>
     1493                    </div>
     1494                </div>
     1495            </div>
     1496            <p style="font-style: italic; color: #666; margin-bottom: 0;">
     1497                Esta é uma pré-visualização. As alterações são aplicadas automaticamente conforme você modifica as configurações acima.
     1498            </p>
     1499        </div>
     1500
     1501        <!-- Ensure the preview gets the same CSS as the real calculator -->
     1502        <style id="superfrete-preview-base-styles">
     1503            /* Copy the calculator styles for preview */
     1504            #super-frete-shipping-calculator-preview {
     1505                background-color: var(--superfrete-bg-color);
     1506                padding: var(--superfrete-spacing-lg);
     1507                border-radius: var(--superfrete-radius-lg);
     1508                margin-bottom: var(--superfrete-spacing-lg);
     1509                max-width: 500px;
     1510                box-shadow: var(--superfrete-shadow-sm);
     1511                font-size: var(--superfrete-font-size-base);
     1512                line-height: var(--superfrete-line-height);
     1513                font-family: var(--superfrete-font-family);
     1514                color: var(--superfrete-text-color);
     1515            }
     1516
     1517            #super-frete-shipping-calculator-preview .superfrete-input-section {
     1518                margin-bottom: var(--superfrete-spacing-md);
     1519            }
     1520
     1521            #super-frete-shipping-calculator-preview #calc_shipping_postcode_field input {
     1522                width: 100%;
     1523                padding: var(--superfrete-spacing-sm) var(--superfrete-spacing-md);
     1524                border: 1px solid var(--superfrete-border-color);
     1525                border-radius: var(--superfrete-radius-sm);
     1526                font-size: var(--superfrete-font-size-base);
     1527                transition: all 0.3s ease;
     1528                letter-spacing: 0.5px;
     1529                font-weight: 500;
     1530                font-family: var(--superfrete-font-family);
     1531                color: var(--superfrete-text-color);
     1532                background-color: var(--superfrete-bg-white);
     1533                box-sizing: border-box;
     1534            }
     1535
     1536            #super-frete-shipping-calculator-preview .superfrete-results-container {
     1537                background-color: var(--superfrete-bg-white);
     1538                border: 1px solid var(--superfrete-border-light);
     1539                border-radius: var(--superfrete-radius-sm);
     1540                padding: var(--superfrete-spacing-md);
     1541                box-shadow: var(--superfrete-shadow-sm);
     1542            }
     1543
     1544            #super-frete-shipping-calculator-preview .superfrete-shipping-methods h3 {
     1545                font-size: var(--superfrete-font-size-base);
     1546                margin-bottom: var(--superfrete-spacing-sm);
     1547                border-bottom: 1px solid var(--superfrete-border-light);
     1548                padding-bottom: var(--superfrete-spacing-sm);
     1549                color: var(--superfrete-heading-color);
     1550                font-weight: 600;
     1551                margin-top: 0;
     1552            }
     1553
     1554            #super-frete-shipping-calculator-preview .superfrete-shipping-method {
     1555                display: flex;
     1556                justify-content: space-between;
     1557                padding: var(--superfrete-spacing-sm) 0;
     1558                border-bottom: 1px solid var(--superfrete-border-light);
     1559                align-items: center;
     1560            }
     1561
     1562            #super-frete-shipping-calculator-preview .superfrete-shipping-method:last-child {
     1563                border-bottom: none;
     1564            }
     1565
     1566            #super-frete-shipping-calculator-preview .superfrete-shipping-method-name {
     1567                font-weight: 600;
     1568                color: var(--superfrete-text-color);
     1569                font-size: var(--superfrete-font-size-small);
     1570                flex: 1;
     1571            }
     1572
     1573            #super-frete-shipping-calculator-preview .superfrete-shipping-method-price {
     1574                font-weight: 600;
     1575                color: var(--superfrete-primary-color);
     1576                font-size: var(--superfrete-font-size-small);
     1577                text-align: right;
     1578            }
     1579        </style>
     1580
     1581        <style id="superfrete-preview-styles">
     1582            /* Dynamic preview styles will be injected here by JavaScript */
     1583        </style>
     1584        <?php
     1585        return ob_get_clean();
    1491586    }
    1501587}
     
    1551592// Hook para adicionar a aba dentro de "Entrega"
    1561593add_filter('woocommerce_shipping_settings', ['SuperFrete_API\Admin\SuperFrete_Settings', 'add_superfrete_settings']);
    157 add_action('admin_footer', ['SuperFrete_API\Admin\SuperFrete_Settings', 'enqueue_admin_scripts']);
     1594add_action('admin_init', ['SuperFrete_API\Admin\SuperFrete_Settings', 'enqueue_admin_scripts']);
     1595
     1596// AJAX hooks for webhook management
     1597add_action('wp_ajax_superfrete_register_webhook', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_webhook_registration']);
     1598add_action('wp_ajax_superfrete_oauth_callback', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_oauth_callback']);
     1599
     1600// AJAX hooks for visual customization
     1601add_action('wp_ajax_superfrete_save_customization', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_save_customization']);
     1602add_action('wp_ajax_superfrete_reset_customization', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_reset_customization']);
  • superfrete/trunk/app/Controllers/ProductShipping.php

    r3289983 r3341621  
    1212
    1313    public function __construct() {
    14        
    15 
    16             add_action('woocommerce_after_add_to_cart_form', array(__CLASS__, 'calculator'));
    17        
    18        
     14
     15        add_action('woocommerce_after_add_to_cart_form', array(__CLASS__, 'calculator'));
    1916        add_shortcode('pi_shipping_calculator', array($this, 'calculator_shortcode'));
    2017       
    21 add_action('wc_ajax_pi_load_location_by_ajax', array(__CLASS__, 'loadLocation') );
     18        add_action('wc_ajax_pi_load_location_by_ajax', array(__CLASS__, 'loadLocation') );
    2219        add_action('woocommerce_after_add_to_cart_form', [$this, 'enqueue_scripts']);
    2320        add_action('wp_ajax_superfrete_calculate', [$this, 'calculate_shipping']);
     
    2724        add_action('wp_ajax_nopriv_superfrete_cal_shipping', array(__CLASS__, 'applyShipping'));
    2825        add_action('wc_ajax_superfrete_cal_shipping', array(__CLASS__, 'applyShipping'));
     26       
     27        // Hook the hideCalculator method to the filter so virtual products are automatically hidden
     28        add_filter('superfrete_hide_calculator_on_single_product_page', array(__CLASS__, 'hideCalculator'), 10, 2);
    2929    }
    3030
     
    5757        global $product;
    5858
     59        // Hide calculator for virtual products (they don't need shipping)
     60        if (is_object($product) && $product->is_virtual()) {
     61            return;
     62        }
     63
    5964        if (apply_filters('superfrete_hide_calculator_on_single_product_page', false, $product)) {
    6065            return;
     
    8994        wp_send_json_error(['message' => 'Requisição inválida.'], 403);
    9095    }
     96        // Start timing the entire function execution
     97        $total_start_time = microtime(true);
     98       
     99        // Setup logging
     100        $log_data = [
     101            'method' => 'applyShipping',
     102            'steps' => [],
     103            'total_time' => 0
     104        ];
     105       
    91106        if (!class_exists('WC_Shortcode_Cart')) {
    92107            include_once WC_ABSPATH . 'includes/shortcodes/class-wc-shortcode-cart.php';
     
    95110       
    96111        if (self::doingCalculation()) {
    97 
    98            
    99               if ((isset($_POST['action_auto_load']) && self::disableAutoLoadEstimate()) || empty($_POST['calc_shipping_postcode']) || !isset($_POST['calc_shipping_postcode'])) {
    100            
     112            $step_start = microtime(true);
     113       
     114            if ((isset($_POST['action_auto_load']) && self::disableAutoLoadEstimate()) || empty($_POST['calc_shipping_postcode']) || !isset($_POST['calc_shipping_postcode'])) {
    101115                $return['shipping_methods'] = sprintf('<div class="superfrete-alert">%s</div>', esc_html(get_option('superfrete_no_address_added_yet', 'Informe seu Endereço para calcular')));
    102116                wp_send_json($return);
    103117            }
     118           
     119            $log_data['steps']['initial_validation'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms';
     120            $step_start = microtime(true);
    104121                   
    105             /*
    106               $return = array();
    107               WC_Shortcode_Cart::calculate_shipping();
    108               WC()->cart->calculate_totals();
    109 
    110 
    111               $item_key = self::addTestProductForProperShippingCost();
    112 
    113 
    114               if(WC()->cart->get_cart_contents_count() == 0 ){
    115               $blank_package = self::get_shipping_packages();
    116               WC()->shipping()->calculate_shipping($blank_package );
    117               }
    118               $packages = WC()->shipping()->get_packages();
    119               $shipping_methods = self::getShippingMethods($packages);
    120               $return['error'] = wc_print_notices(true);
    121               //error_log(print_r($return,true));
    122               //wc_clear_notices();
    123               $return['shipping_methods'] = self::messageTemplate($shipping_methods);
    124               echo json_encode($return);
    125 
    126               if($item_key){
    127               WC()->cart->remove_cart_item($item_key);
    128               }
    129              */
    130 
    131122            $return = array();
    132123            \WC_Shortcode_Cart::calculate_shipping();
    133124            WC()->cart->calculate_totals();
    134125 
    135          
    136 
     126            $log_data['steps']['calculate_shipping'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms';
     127            $step_start = microtime(true);
    137128   
    138                     $original_cart = WC()->cart->get_cart();
    139                     WC()->cart->empty_cart();
    140                     $item_key = self::addTestProductForProperShippingCost();
    141 
    142                     WC()->shipping()->calculate_shipping();
    143                     WC()->cart->calculate_totals();
    144                     $packages = WC()->shipping()->get_packages();
     129            // OPTIMIZATION: Use a lightweight shipping package instead of manipulating the cart
     130            // This avoids expensive cart operations that were causing performance issues
     131           
     132            // Get product ID and variation ID
     133            $product_id = filter_input(INPUT_POST, 'product_id', FILTER_VALIDATE_INT);
     134            $variation_id = filter_input(INPUT_POST, 'variation_id', FILTER_VALIDATE_INT);
     135            $quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT) ?: 1;
     136           
     137            // Get product data directly without cart manipulation
     138            if ($product_id) {
     139                $product = wc_get_product($variation_id ?: $product_id);
     140               
     141                if ($product) {
     142                    // Skip calculation for virtual products
     143                    if ($product->is_virtual()) {
     144                        $return['shipping_methods'] = sprintf('<div class="superfrete-alert">%s</div>', esc_html__('Produtos virtuais não necessitam de frete.', 'superfrete'));
     145                        wp_send_json($return);
     146                    }
     147                    // Get destination from form data (not customer data)
     148                    $destination_postcode = sanitize_text_field($_POST['calc_shipping_postcode'] ?? '');
     149                    $destination_country = sanitize_text_field($_POST['calc_shipping_country'] ?? 'BR');
     150                    $destination_state = sanitize_text_field($_POST['calc_shipping_state'] ?? '');
     151                    $destination_city = sanitize_text_field($_POST['calc_shipping_city'] ?? '');
     152                   
     153                    // Create a lightweight package for shipping calculation
     154                    $package = [
     155                        'contents' => [
     156                            [
     157                                'data' => $product,
     158                                'quantity' => $quantity
     159                            ]
     160                        ],
     161                        'contents_cost' => $product->get_price() * $quantity,
     162                        'applied_coupons' => [],
     163                        'user' => [
     164                            'ID' => get_current_user_id(),
     165                        ],
     166                        'destination' => [
     167                            'country' => $destination_country,
     168                            'state' => $destination_state,
     169                            'postcode' => $destination_postcode,
     170                            'city' => $destination_city,
     171                        ],
     172                        'cart_subtotal' => $product->get_price() * $quantity,
     173                    ];
     174                   
     175                    $log_data['steps']['prepare_package'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms';
     176                    $step_start = microtime(true);
     177                   
     178                    // Debug: Check all registered shipping methods
     179                    $wc_shipping = \WC_Shipping::instance();
     180                    $all_shipping_methods = $wc_shipping->get_shipping_methods();
     181                    $log_data['all_registered_methods'] = array_keys($all_shipping_methods);
     182                   
     183                    // Debug: Check all shipping zones
     184                    $all_zones = \WC_Shipping_Zones::get_zones();
     185                    $log_data['all_zones'] = [];
     186                    foreach ($all_zones as $zone_data) {
     187                        $zone = new \WC_Shipping_Zone($zone_data['zone_id']);
     188                        $zone_methods = [];
     189                        foreach ($zone->get_shipping_methods() as $method) {
     190                            $zone_methods[] = [
     191                                'id' => $method->id,
     192                                'enabled' => $method->enabled,
     193                                'title' => $method->get_title(),
     194                            ];
     195                        }
     196                        $log_data['all_zones'][] = [
     197                            'id' => $zone->get_id(),
     198                            'name' => $zone->get_zone_name(),
     199                            'methods' => $zone_methods,
     200                        ];
     201                    }
     202                   
     203                    // Calculate shipping rates directly with our package
     204                    $shipping_zone = \WC_Shipping_Zones::get_zone_matching_package($package);
     205                   
     206                    // Add detailed debugging for shipping zone
     207                    $log_data['package_destination'] = $package['destination'];
     208                    $log_data['shipping_zone'] = [
     209                        'id' => $shipping_zone->get_id(),
     210                        'name' => $shipping_zone->get_zone_name(),
     211                        'locations' => $shipping_zone->get_zone_locations(),
     212                    ];
     213                   
     214                    // Detailed logging for shipping calculation
     215                    $shipping_start = microtime(true);
     216                   
     217                    // Log shipping providers being used
     218                    $active_shipping = [];
     219                    $all_methods = $shipping_zone->get_shipping_methods(true);
     220                    foreach ($all_methods as $method) {
     221                        $active_shipping[] = [
     222                            'id' => $method->id,
     223                            'enabled' => $method->is_enabled() ? 'yes' : 'no',
     224                            'title' => $method->get_title(),
     225                            'instance_id' => $method->get_instance_id(),
     226                        ];
     227                    }
     228                    $log_data['active_shipping_methods'] = $active_shipping;
     229                    $log_data['total_methods_found'] = count($all_methods);
     230                   
     231                    // Calculate shipping directly
     232                    $rates = [];
     233                    $method_times = [];
     234                   
     235                    foreach ($shipping_zone->get_shipping_methods(true) as $method) {
     236                        $method_start = microtime(true);
     237                        $method_id = $method->id;
     238                       
     239                        // Log method details
     240                        $log_data['shipping_methods'][] = [
     241                            'id' => $method_id,
     242                            'title' => $method->get_title(),
     243                            'enabled' => $method->is_enabled() ? 'yes' : 'no',
     244                            'class' => get_class($method),
     245                            'instance_id' => $method->get_instance_id(),
     246                        ];
     247                       
     248                        // Time each individual method
     249                        try {
     250                            // For SuperFrete shipping method, call calculate_shipping directly
     251                            if ($method_id === 'superfrete_shipping') {
     252                                $method->calculate_shipping($package);
     253                                $method_rates = $method->rates;
     254                            } else {
     255                                $method_rates = $method->get_rates_for_package($package);
     256                            }
     257                           
     258                            $log_data['method_results'][$method_id] = [
     259                                'rates_count' => is_array($method_rates) ? count($method_rates) : 0,
     260                                'rates' => $method_rates ? array_keys($method_rates) : [],
     261                            ];
     262                        } catch (Exception $e) {
     263                            $log_data['method_errors'][$method_id] = $e->getMessage();
     264                            $method_rates = [];
     265                        }
     266                       
     267                        $method_time = round((microtime(true) - $method_start) * 1000, 2);
     268                        $method_times[$method_id] = $method_time . ' ms';
     269                       
     270                        // Log any methods taking over 500ms
     271                        if ($method_time > 500) {
     272                            $log_data['slow_methods'][$method_id] = $method_time . ' ms';
     273                        }
     274                       
     275                        if ($method_rates) {
     276                            $rates = array_merge($rates, $method_rates);
     277                        }
     278                    }
     279                   
     280                    // Add method timing to logs
     281                    $log_data['method_times'] = $method_times;
     282                   
     283                    // Calculate shipping time first
     284                    $shipping_time = round((microtime(true) - $shipping_start) * 1000, 2);
     285                   
     286                    // Check for common API bottlenecks
     287                    if ($shipping_time > 1000) {
     288                        // Get the HTTP stats to see external API calls
     289                        global $wp_version;
     290                        $http_counts = [
     291                            'total_requests' => 0,
     292                            'total_time' => 0,
     293                            'average_time' => 0
     294                        ];
     295                       
     296                        // If using WordPress HTTP API, we can check the stats
     297                        if (function_exists('_get_http_stats')) {
     298                            $http_stats = _get_http_stats();
     299                            if (isset($http_stats['requests'])) {
     300                                $http_counts['total_requests'] = count($http_stats['requests']);
    145301                               
    146    
    147            
    148            
    149 
    150             $shipping_methods = self::getShippingMethods($packages);
    151             $return['error'] = wc_print_notices(true);
    152             $return['shipping_methods'] = self::messageTemplate($shipping_methods);
     302                                // Sum up all request times
     303                                foreach ($http_stats['requests'] as $request) {
     304                                    if (isset($request['args']['timeout'])) {
     305                                        // Log timeout values
     306                                        $http_counts['timeouts'][] = $request['args']['timeout'];
     307                                    }
     308                                    if (isset($request['end_time']) && isset($request['start_time'])) {
     309                                        $request_time = $request['end_time'] - $request['start_time'];
     310                                        $http_counts['total_time'] += $request_time;
     311                                       
     312                                        // Log slow individual requests (over 500ms)
     313                                        if ($request_time > 0.5) {
     314                                            $http_counts['slow_requests'][] = [
     315                                                'url' => isset($request['url']) ? preg_replace('/\?.*/', '', $request['url']) : 'unknown',
     316                                                'time' => round($request_time * 1000) . ' ms'
     317                                            ];
     318                                        }
     319                                    }
     320                                }
     321                               
     322                                if ($http_counts['total_requests'] > 0) {
     323                                    $http_counts['average_time'] = round(($http_counts['total_time'] / $http_counts['total_requests']) * 1000, 2) . ' ms';
     324                                    $http_counts['total_time'] = round($http_counts['total_time'] * 1000, 2) . ' ms';
     325                                }
     326                            }
     327                        }
     328                       
     329                        $log_data['http_api'] = $http_counts;
     330                    }
     331                   
     332                    $log_data['steps']['calculate_shipping_only'] = $shipping_time . ' ms';
     333                   
     334                    // Log potential slow API calls
     335                    if ($shipping_time > 2000) { // If over 2 seconds
     336                        $log_data['warning'] = 'Shipping calculation is slow - may indicate API rate limiting or network issues';
     337                    }
     338                   
     339                    $log_data['steps']['calculate_test_shipping'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms';
     340                    $step_start = microtime(true);
     341                   
     342                    // Format shipping methods
     343                    $shipping_methods = [];
     344                    foreach ($rates as $rate_id => $rate) {
     345                        $title = wc_cart_totals_shipping_method_label($rate);
     346                        $title = self::modifiedTitle($title, $rate);
     347                        $shipping_methods[$rate_id] = apply_filters('superfrete_ppscw_shipping_method_name', $title, $rate, $product_id, $variation_id);
     348                    }
     349                   
     350                    $log_data['steps']['get_shipping_methods'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms';
     351                    $step_start = microtime(true);
     352                   
     353                    $return['error'] = ''; // Empty the error to prevent notifications
     354                    $return['shipping_methods'] = self::messageTemplate($shipping_methods);
     355                   
     356                    $log_data['steps']['format_methods_html'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms';
     357                   
     358                    // No need to restore cart or remove items - we never changed it
     359                    $log_data['steps']['restore_cart'] = 0;
     360                } else {
     361                    $return['error'] = __('Produto não encontrado.', 'superfrete');
     362                    $return['shipping_methods'] = '';
     363                }
     364            } else {
     365                $return['error'] = __('ID do produto não fornecido.', 'superfrete');
     366                $return['shipping_methods'] = '';
     367            }
     368           
     369            // Calculate total execution time
     370            $log_data['total_time'] = round((microtime(true) - $total_start_time) * 1000, 2) . ' ms';
     371           
     372            // Add log data to the return for debugging
     373            $return['performance_log'] = $log_data;
     374           
     375            // Log to the WordPress error log
     376            error_log('SuperFrete Performance Log: ' . wp_json_encode($log_data));
     377           
    153378            echo wp_json_encode($return);
    154 
    155             if (!empty($original_cart)) {
    156                 WC()->cart->set_cart_contents($original_cart);
    157                 WC()->cart->calculate_totals();
    158             }
    159 
    160             if ($item_key) {
    161                 WC()->cart->remove_cart_item($item_key);
    162             }
    163379        }
    164380        wp_die();
     
    362578
    363579    static function disableAutoLoadEstimate() {
    364         $auto_loading = get_option('superfrete_auto_calculation', 'enabled');
     580        $auto_loading = get_option('superfrete_auto_calculation', 'disabled'); // Changed default to disabled
    365581
    366582        if ($auto_loading == 'enabled')
     
    371587
    372588    static function messageTemplate($shipping_methods) {
    373 
    374 
    375 
    376         $message_above = get_option('superfrete_above_shipping_methods', 'Metódos de Envio Disponíveis');
    377 
    378         $message_above = self::shortCode($message_above);
    379 
    380         if (is_array($shipping_methods) && !empty($shipping_methods)) {
    381             $html = '';
    382             foreach ($shipping_methods as $id => $method) {
    383                 $html .= sprintf('<li id="%s">%s</li>', esc_attr($id), $method);
    384             }
    385             if (!empty($html)) {
    386                 $shipping_methods_msg = '<ul class="superfrete-methods">' . $html . '</ul>';
    387             } else {
    388                 $shipping_methods_msg = "";
    389             }
     589        $html = '';
     590        if (!empty($shipping_methods)) {
     591            $html .= '<div class="superfrete-shipping-methods">';
     592            $html .= '<h3>' . __('Opções de Entrega', 'superfrete') . '</h3>';
     593           
     594            foreach ($shipping_methods as $method_id => $method_name) {
     595                // Extract shipping method name and price
     596                $html .= '<div class="superfrete-shipping-method">';
     597               
     598                // Get just the method name (everything before the colon)
     599                $method_parts = explode(':', $method_name, 2);
     600                $method_label = trim(strip_tags($method_parts[0]));
     601               
     602                // Add method name to the HTML
     603                $html .= '<span class="superfrete-shipping-method-name">' . esc_html($method_label) . '</span>';
     604               
     605                // Handle price display - keeping the full HTML structure for WooCommerce price formatting
     606                if (count($method_parts) > 1 && !empty($method_parts[1])) {
     607                    // Check if the price part contains HTML or not
     608                    if (strpos($method_parts[1], '<span') !== false) {
     609                        // Keep the HTML formatting intact for the price
     610                        $html .= '<span class="superfrete-shipping-method-price">' . wp_kses_post($method_parts[1]) . '</span>';
     611                    } else {
     612                        // Add simple text price
     613                        $html .= '<span class="superfrete-shipping-method-price">' . esc_html(trim($method_parts[1])) . '</span>';
     614                    }
     615                } else {
     616                    // No price found, show as Free
     617                    $html .= '<span class="superfrete-shipping-method-price">Gratuito</span>';
     618                }
     619               
     620                $html .= '</div>';
     621            }
     622           
     623            $html .= '</div>';
    390624        } else {
    391             $shipping_methods_msg = "";
    392         }
    393 
    394         $when_shipping_msg = wp_kses_post($message_above) . '<br>' . $shipping_methods_msg;
    395 
    396         $msg = is_array($shipping_methods) && !empty($shipping_methods) ? $when_shipping_msg : self::noMethodAvailableMsg();
    397 
    398         return sprintf('<div class="superfrete-alert">%s</div>', $msg);
     625            $message = get_option('superfrete_no_rates_message', 'Desculpe, não encontramos métodos de envio para [country]. Por favor, verifique seu endereço ou entre em contato conosco.');
     626           
     627            $country = __('Brasil', 'superfrete');
     628
     629            if (isset(WC()->customer)) {
     630                $country_code = self::get_customer()->get_shipping_country();
     631                if (!empty($country_code) && isset(WC()->countries) && $country_code !== 'default') {
     632                    $country = WC()->countries->countries[$country_code];
     633                }
     634            }
     635
     636            $find_replace = [
     637                '[country]' => $country
     638            ];
     639
     640            $message = str_replace(array_keys($find_replace), array_values($find_replace), $message);
     641           
     642            $html .= '<div class="superfrete-no-shipping-methods">' . esc_html($message) . '</div>';
     643        }
     644
     645        return $html;
    399646    }
    400647
     
    466713     */
    467714    public function enqueue_scripts() {
    468             $plugin_file = plugin_dir_path(__FILE__) . '../../superfrete.php';
    469 
    470     // Inclui função get_plugin_data se ainda não estiver disponível
    471     if ( ! function_exists( 'get_plugin_data' ) ) {
    472         require_once ABSPATH . 'wp-admin/includes/plugin.php';
    473     }
    474           $plugin_data = get_plugin_data( $plugin_file );
    475     $plugin_version = $plugin_data['Version'];
     715        $plugin_file = plugin_dir_path(__FILE__) . '../../superfrete.php';
     716
     717        // Inclui função get_plugin_data se ainda não estiver disponível
     718        if (!function_exists('get_plugin_data')) {
     719            require_once ABSPATH . 'wp-admin/includes/plugin.php';
     720        }
     721        $plugin_data = get_plugin_data($plugin_file);
     722        $plugin_version = $plugin_data['Version'];
     723       
     724        // Enqueue JavaScript
    476725        wp_enqueue_script(
    477                 'superfrete-calculator',
    478                 plugin_dir_url(__FILE__) . '../../assets/scripts/superfrete-calculator.js',
    479                 ['jquery'],
    480               $plugin_version, // Versão do script
    481                 null,
    482                 true
     726            'superfrete-calculator',
     727            plugin_dir_url(__FILE__) . '../../assets/scripts/superfrete-calculator.js',
     728            ['jquery'],
     729            $plugin_version, // Versão do script
     730            true
     731        );
     732
     733        // Enqueue CSS
     734        wp_enqueue_style(
     735            'superfrete-calculator-style',
     736            plugin_dir_url(__FILE__) . '../../assets/styles/superfrete-calculator.css',
     737            [],
     738            $plugin_version
    483739        );
    484740
     
    486742            'ajax_url' => admin_url('admin-ajax.php'),
    487743        ]);
     744       
     745        // Localize script with additional settings
     746        wp_localize_script('superfrete-calculator', 'superfrete_setting', [
     747            'wc_ajax_url' => add_query_arg(['wc-ajax' => '%%endpoint%%'], home_url('/')),
     748            'auto_load' => true,
     749            'country_code' => 'BR',
     750            'i18n' => [
     751                'state_label' => __('Estado', 'superfrete'),
     752                'select_state_text' => __('Selecione um estado', 'superfrete')
     753            ]
     754        ]);
    488755    }
    489756}
  • superfrete/trunk/app/Controllers/SuperFrete_Order.php

    r3289983 r3341621  
    66use SuperFrete_API\Helpers\Logger;
    77use SuperFrete_API\Helpers\SuperFrete_Notice;
     8use SuperFrete_API\Helpers\AddressHelper;
    89use WC_Order;
    910
     
    3334            return;
    3435
    35         $superfrete_status = get_post_meta($order_id, '_superfrete_status', true);
     36        $order = wc_get_order($order_id);
     37        $superfrete_status = $order ? $order->get_meta('_superfrete_status') : '';
    3638        if ($superfrete_status == 'enviado')
    3739            return;
     
    7173        // Verifica e adiciona os campos personalizados
    7274        if (empty($shipping['number'])) {
    73             $shipping['number'] = $order->get_meta('_shipping_number');
    74         }
     75            // Try multiple possible field names for number
     76            $possible_number_fields = [
     77                '_shipping_number',
     78                '_billing_number',
     79                '_WC_OTHER/SHIPPING/NUMBER',
     80                'shipping/number',
     81                'billing_number'
     82            ];
     83           
     84            foreach ($possible_number_fields as $field) {
     85                $value = $order->get_meta($field);
     86                Logger::log('SuperFrete', 'Checking number field ' . $field . ' for order #' . $order_id . ': "' . $value . '"');
     87                if (!empty($value)) {
     88                    $shipping['number'] = $value;
     89                    Logger::log('SuperFrete', 'Using number from field ' . $field . ' for order #' . $order_id . ': ' . $value);
     90                    break;
     91                }
     92            }
     93           
     94            // If still empty, search directly in meta data
     95            if (empty($shipping['number'])) {
     96                $all_meta = $order->get_meta_data();
     97                foreach ($all_meta as $meta) {
     98                    if (strpos(strtolower($meta->key), 'number') !== false && !empty($meta->value)) {
     99                        $shipping['number'] = $meta->value;
     100                        Logger::log('SuperFrete', 'Found number via meta search for order #' . $order_id . ' (key: ' . $meta->key . '): ' . $meta->value);
     101                        break;
     102                    }
     103                }
     104            }
     105        }
     106       
    75107        if (empty($shipping['neighborhood'])) {
    76             $shipping['neighborhood'] = $order->get_meta('_shipping_neighborhood');
    77         }
     108            // Try multiple possible field names for neighborhood
     109            $possible_neighborhood_fields = [
     110                '_shipping_neighborhood',
     111                '_billing_neighborhood',
     112                '_WC_OTHER/SHIPPING/NEIGHBORHOOD',
     113                'shipping/neighborhood',
     114                'billing_neighborhood'
     115            ];
     116           
     117            foreach ($possible_neighborhood_fields as $field) {
     118                $value = $order->get_meta($field);
     119                Logger::log('SuperFrete', 'Checking neighborhood field ' . $field . ' for order #' . $order_id . ': "' . $value . '"');
     120                if (!empty($value)) {
     121                    $shipping['neighborhood'] = $value;
     122                    Logger::log('SuperFrete', 'Using neighborhood from field ' . $field . ' for order #' . $order_id . ': ' . $value);
     123                    break;
     124                }
     125            }
     126           
     127            // If still empty, search directly in meta data
     128            if (empty($shipping['neighborhood'])) {
     129                $all_meta = $order->get_meta_data();
     130                foreach ($all_meta as $meta) {
     131                    if (strpos(strtolower($meta->key), 'neighborhood') !== false && !empty($meta->value)) {
     132                        $shipping['neighborhood'] = $meta->value;
     133                        Logger::log('SuperFrete', 'Found neighborhood via meta search for order #' . $order_id . ' (key: ' . $meta->key . '): ' . $meta->value);
     134                        break;
     135                    }
     136                }
     137            }
     138        }
     139       
     140        // If district is still missing, try to get it from ViaCEP
     141        if (empty($shipping['neighborhood']) && !empty($shipping['postcode'])) {
     142            Logger::log('SuperFrete', 'District missing for order #' . $order_id . ', trying ViaCEP for CEP: ' . $shipping['postcode']);
     143            $district_from_viacep = AddressHelper::get_district_from_postal_code($shipping['postcode']);
     144            if ($district_from_viacep) {
     145                $shipping['neighborhood'] = $district_from_viacep;
     146                Logger::log('SuperFrete', 'Got district from ViaCEP for order #' . $order_id . ': ' . $district_from_viacep);
     147            } else {
     148                Logger::log('SuperFrete', 'Could not get district from ViaCEP for order #' . $order_id . ' with CEP: ' . $shipping['postcode']);
     149            }
     150        }
     151       
     152        // Get customer document (CPF/CNPJ)
     153        $document = '';
     154       
     155        // Search through all meta data for document field
     156        $all_meta = $order->get_meta_data();
     157        foreach ($all_meta as $meta) {
     158            // Check if this could be our document field
     159            if (strpos($meta->key, 'DOCUMENT') !== false || strpos($meta->key, 'document') !== false) {
     160                $clean_value = preg_replace('/[^0-9]/', '', $meta->value);
     161               
     162                if (strlen($clean_value) == 11 || strlen($clean_value) == 14) {
     163                    $document = $clean_value;
     164                    Logger::log('SuperFrete', 'Document found for order #' . $order_id . ': ' . substr($document, 0, 3) . '***');
     165                    break;
     166                }
     167            }
     168        }
     169       
     170        // If no document found, log warning
     171        if (empty($document)) {
     172            Logger::log('SuperFrete', 'Warning: No CPF/CNPJ found for order #' . $order_id);
     173        }
     174       
    78175        $destinatario = [
    79176            'name' => $shipping['first_name'] . ' ' . $shipping['last_name'],
     
    86183            'postal_code' => preg_replace('/[^\p{L}\p{N}\s]/', '', $shipping['postcode'])
    87184        ];
     185       
     186        // Add document if available
     187        if (!empty($document)) {
     188            $destinatario['document'] = $document;
     189            Logger::log('SuperFrete', 'Document added to destinatario for order #' . $order_id . ': ' . substr($document, 0, 3) . '***');
     190        } else {
     191            Logger::log('SuperFrete', 'WARNING: No document found to add to destinatario for order #' . $order_id);
     192        }
    88193
    89194        // Obtém o método de envio escolhido
     
    92197
    93198        foreach ($chosen_methods as $method) {
    94 
    95 
    96             if (strpos(strtolower($method->get_name()), 'pac') !== false) {
    97                 $service = "1";
    98             } elseif (strpos(strtolower($method->get_name()), 'sedex') !== false) {
    99                 $service = "2";
    100             } elseif (strpos(strtolower($method->get_name()), 'mini envios') !== false) {
    101                 $service = "17";
     199            // First try to get service ID from method metadata
     200            $method_data = $method->get_data();
     201            if (isset($method_data['meta_data']) && is_array($method_data['meta_data'])) {
     202                foreach ($method_data['meta_data'] as $meta) {
     203                    if ($meta->key === 'service_id') {
     204                        $service = strval($meta->value);
     205                        Logger::log('SuperFrete', 'Got service ID from metadata for order #' . $order_id . ': ' . $service);
     206                        break 2; // Exit both loops
     207                    }
     208                }
     209            }
     210           
     211            // Fallback to name-based detection if no metadata
     212            if (empty($service)) {
     213                $method_id = $method->get_method_id();
     214                $method_name = strtolower($method->get_name());
     215               
     216                // Check method ID first (more reliable)
     217                if (strpos($method_id, 'superfrete_pac') !== false) {
     218                    $service = "1";
     219                } elseif (strpos($method_id, 'superfrete_sedex') !== false) {
     220                    $service = "2";
     221                } elseif (strpos($method_id, 'superfrete_jadlog') !== false) {
     222                    $service = "3";
     223                } elseif (strpos($method_id, 'superfrete_mini_envio') !== false) {
     224                    $service = "17";
     225                } elseif (strpos($method_id, 'superfrete_loggi') !== false) {
     226                    $service = "31";
     227                }
     228                // Fallback to name checking
     229                elseif (strpos($method_name, 'pac') !== false) {
     230                    $service = "1";
     231                } elseif (strpos($method_name, 'sedex') !== false) {
     232                    $service = "2";
     233                } elseif (strpos($method_name, 'jadlog') !== false) {
     234                    $service = "3";
     235                } elseif (strpos($method_name, 'mini envio') !== false) {
     236                    $service = "17";
     237                } elseif (strpos($method_name, 'loggi') !== false) {
     238                    $service = "31";
     239                }
     240               
     241                Logger::log('SuperFrete', 'Service ID for order #' . $order_id . ' determined from method ID/name: ' . $service . ' (method_id: ' . $method_id . ', name: ' . $method_name . ')');
    102242            }
    103243        }
     
    186326            'to' => $destinatario,
    187327            'email' => $order->get_billing_email(),
    188             'service' => $service,
     328            'service' => intval($service),
    189329            'products' => $produtos,
    190330            'volumes' => $volume_data,
     
    251391
    252392        Logger::log('SuperFrete', 'Resposta da API para o pedido #' . $order_id . ': ' . wp_json_encode($response));
     393
    253394        if ($response) {
    254             update_post_meta($order_id, '_superfrete_id', $response['id']);
    255             update_post_meta($order_id, '_superfrete_protocol', $response['protocol']);
    256             update_post_meta($order_id, '_superfrete_price', $response['price']);
    257             update_post_meta($order_id, '_superfrete_status', 'enviado');
    258         }
     395            $order = wc_get_order($order_id);
     396            if ($order) {
     397                $order->update_meta_data('_superfrete_id', $response['id']);
     398                $order->update_meta_data('_superfrete_protocol', $response['protocol']);
     399                $order->update_meta_data('_superfrete_price', $response['price']);
     400                $order->update_meta_data('_superfrete_status', 'enviado');
     401                $order->save();
     402            }
     403        }
     404
    259405        return $response;
    260406    }
  • superfrete/trunk/app/Helpers/SuperFrete_Notice.php

    r3289983 r3341621  
    1313     */
    1414    public static function add_error($order_id, $message, $missing_fields = []) {
    15         if (!session_id()) {
     15        if (!session_id() && !headers_sent()) {
    1616            session_start();
    1717        }
     
    2828     */
    2929public static function display_errors() {
    30     if (!session_id()) {
     30    if (!session_id() && !headers_sent()) {
    3131        session_start();
    3232    }
  • superfrete/trunk/app/Shipping/SuperFreteMiniEnvio.php

    r3289983 r3341621  
    33namespace SuperFrete_API\Shipping;
    44
    5 use SuperFrete_API\Http\Request;
    6 use SuperFrete_API\Helpers\Logger;
    7 
    85if (!defined('ABSPATH'))
    96    exit; // Segurança
    107
    11 class SuperFreteMiniEnvios extends \WC_Shipping_Method {
    12     public $free_shipping;
    13     public $extra_days;
    14     public $extra_cost;
    15     public $extra_cost_type;
    16    
    17  public function __construct( $instance_id = 'superfrete_mini_envio') {
    18         $this->id                    = 'superfrete_mini_envio';
    19         $this->instance_id           = absint( $instance_id );
    20         $this->method_title          = __( 'Mini Envios SuperFrete' );
    21         $this->method_description    = __( 'Envia utilizando Mini Envios' );
    22         $this->supports              = array(
    23             'shipping-zones',
    24             'instance-settings',
    25         );
    26         $this->instance_form_fields = array(
    27             'enabled' => array(
    28                 'title'         => __( 'Ativar/Desativar' ),
    29                 'type'             => 'checkbox',
    30                 'label'         => __('Ativar SuperFrete nas áreas de entrega', 'superfrete'),
    31                 'default'         => 'yes',
    32             ),
    33             'title' => array(
    34                 'title'         => __( 'Method Title' ),
    35                 'type'             => 'text',
    36                   'description' => __('Este título aparecerá no checkout', 'superfrete'),
    37                 'default' => __('Entrega via Mini Envios SuperFrete', 'superfrete'),
    38                 'desc_tip'        => true
    39             ),
    40               'free_shipping' => array(
    41                 'title' => __('Frete Grátis', 'superfrete'),
    42                 'type' => 'checkbox',
    43                 'label' => __('Ativar frete grátis para este método', 'superfrete'),
    44          
    45             ),'extra_days' => array(
    46                 'title' => __('Dias extras no prazo', 'superfrete'),
    47                 'type' => 'number',
    48                 'description' => __('Dias adicionais ao prazo estimado pela SuperFrete', 'superfrete'),
    49                 'default' => 0,
    50                 'desc_tip' => true
    51             ),
    52             'extra_cost' => array(
    53                 'title' => __('Valor adicional no frete', 'superfrete'),
    54                 'type' => 'price',
    55                 'description' => __('Valor extra a ser somado ao custo do frete', 'superfrete'),
    56                 'default' => '0',
    57                 'desc_tip' => true
    58             ),
    59             'extra_cost_type' => array(
    60                 'title' => __('Tipo de valor adicional', 'superfrete'),
    61                 'type' => 'select',
    62                 'description' => __('Escolha se o valor adicional será fixo (R$) ou percentual (%) sobre o frete original.', 'superfrete'),
    63                 'default' => 'fixed',
    64                 'options' => array(
    65                     'fixed' => __('Valor fixo (R$)', 'superfrete'),
    66                     'percent' => __('Porcentagem (%)', 'superfrete'),
    67                 ),
    68             ),
    69         );
    70         $this->enabled = $this->get_option('enabled');
    71         $this->free_shipping = $this->get_option('free_shipping');
    72         $this->title = $this->get_option('title');
    73         $this->extra_days = $this->get_option('extra_days', 0);
    74         $this->extra_cost = $this->get_option('extra_cost', 0);
    75         $this->extra_cost_type = $this->get_option('extra_cost_type', 'fixed');
     8require_once plugin_dir_path(__FILE__) . 'SuperFreteBase.php';
    769
    77         add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
     10class SuperFreteMiniEnvios extends SuperFreteBase {
     11
     12    public function __construct($instance_id = 0) {
     13        $this->id = 'superfrete_mini_envio';
     14        $this->method_title = __('Mini Envios SuperFrete');
     15        $this->method_description = __('Envia utilizando Mini Envios');
     16       
     17        parent::__construct($instance_id);
    7818    }
     19
    7920    protected function get_service_id() {
    8021        return 17; // ID do Mini Envios na API
    8122    }
    82 
    83 
    84     /**
    85      * Calcula o frete e retorna as opções disponíveis.
    86      */
    87       public function calculate_shipping($package = []) {
    88 
    89         if (!$this->enabled || empty($package['destination']['postcode'])) {
    90             return;
    91         }
    92 
    93         $cep_destino = $package['destination']['postcode'];
    94         $peso_total = 0;
    95 $produtos = [];
    96      $insurance_value = 0;
    97 foreach ($package['contents'] as $item) {
    98     $product = $item['data'];
    99    
    100    
    101    
    102    
    103    
    104    $insurance_value += $product->get_price() * $item['quantity'];
    105 $weight_unit = get_option('woocommerce_weight_unit');
    106 $dimension_unit = get_option('woocommerce_dimension_unit');
    107 
    108     $produtos[] = [
    109         'quantity' => $item['quantity'],
    110       'weight'   => ($weight_unit === 'g') ? floatval($product->get_weight()) / 1000 : floatval($product->get_weight()),
    111     'height'   => ($dimension_unit === 'm') ? floatval($product->get_height()) * 100 : floatval($product->get_height()),
    112     'width'    => ($dimension_unit === 'm') ? floatval($product->get_width()) * 100 : floatval($product->get_width()),
    113     'length'   => ($dimension_unit === 'm') ? floatval($product->get_length()) * 100 : floatval($product->get_length()),
    114 
    115     ];
    11623}
    117 
    118 $cep_origem = get_option('woocommerce_store_postcode');
    119 
    120 if (!$cep_origem) {
    121     return;
    122 }
    123 
    124 $payload = [
    125     'from' => ['postal_code' => $cep_origem],
    126     'to' => ['postal_code' => $cep_destino],
    127     'services' => "17",
    128     'options'=> [
    129       "own_hand"=>false,
    130       "receipt"=>false,
    131       "insurance_value"=> $insurance_value,
    132       "use_insurance_value"=>false
    133           ],
    134 
    135     'products' => $produtos,
    136 ];
    137 
    138 $request = new Request();
    139 
    140 
    141 $response = $request->call_superfrete_api('/api/v0/calculator', 'POST', $payload);
    142 
    143         if (!empty($response)) {
    144             foreach ($response as $frete) {
    145                 $prazo_total = $frete['delivery_time'] + $this->extra_days;
    146                 $text_dias = ($prazo_total <= 1) ? "dia útil" : "dias úteis";
    147 
    148                 if (!$frete['has_error'] && !isset($frete["error"])) {
    149                    $frete_custo = 0;
    150 
    151 if ($this->free_shipping !== 'yes') {
    152     $frete_base = floatval($frete['price']);
    153     if ($this->extra_cost_type === 'percent') {
    154         $frete_custo = $frete_base + ($frete_base * ($this->extra_cost / 100));
    155     } else {
    156         $frete_custo = $frete_base + $this->extra_cost;
    157     }
    158 }
    159 
    160                     $frete_desc = ($this->free_shipping === 'yes') ? "- Frete Grátis" : "";
    161 
    162                     $rate = [
    163                         'id' => $this->id . '_' . $frete['id'],
    164                         'label' => $frete['name'] . ' - Promocional - (' . $prazo_total . ' ' . $text_dias . ') ' . $frete_desc,
    165                         'cost' => $frete_custo
    166                     ];
    167                     $this->add_rate($rate);
    168                 }
    169             }
    170         }
    171     }
    172 
    173     public function process_admin_options() {
    174         parent::process_admin_options(); // Chama a função do WooCommerce para salvar
    175     }
    176 }
  • superfrete/trunk/app/Shipping/SuperFretePAC.php

    r3289983 r3341621  
    33namespace SuperFrete_API\Shipping;
    44
    5 use SuperFrete_API\Http\Request;
    6 use SuperFrete_API\Helpers\Logger;
    7 
    85if (!defined('ABSPATH'))
    96    exit; // Segurança
    107
    11 class SuperFretePAC extends \WC_Shipping_Method {
     8require_once plugin_dir_path(__FILE__) . 'SuperFreteBase.php';
    129
    13     public $free_shipping;
    14     public $extra_days;
    15     public $extra_cost;
    16     public $extra_cost_type;
    17  public function __construct( $instance_id = 'superfrete_pac') {
    18         $this->id                    = 'superfrete_pac';
    19         $this->instance_id           = absint( $instance_id );
    20         $this->method_title          = __( 'PAC SuperFrete' );
    21         $this->method_description    = __( 'Envia utilizando PAC' );
    22         $this->supports              = array(
    23             'shipping-zones',
    24             'instance-settings',
    25         );
    26         $this->instance_form_fields = array(
    27             'enabled' => array(
    28                 'title'         => __( 'Ativar/Desativar' ),
    29                 'type'             => 'checkbox',
    30                 'label'         => __('Ativar SuperFrete nas áreas de entrega', 'superfrete'),
    31                 'default'         => 'yes',
    32             ),
    33             'title' => array(
    34                 'title'         => __( 'Method Title' ),
    35                 'type'             => 'text',
    36                   'description' => __('Este título aparecerá no checkout', 'superfrete'),
    37                 'default' => __('Entrega via PAC SuperFrete', 'superfrete'),
    38                 'desc_tip'        => true
    39             ),
    40               'free_shipping' => array(
    41                 'title' => __('Frete Grátis', 'superfrete'),
    42                 'type' => 'checkbox',
    43                 'label' => __('Ativar frete grátis para este método', 'superfrete'),
    44          
    45             ),
    46             'extra_days' => array(
    47                 'title' => __('Dias extras no prazo', 'superfrete'),
    48                 'type' => 'number',
    49                 'description' => __('Dias adicionais ao prazo estimado pela SuperFrete', 'superfrete'),
    50                 'default' => 0,
    51                 'desc_tip' => true
    52             ),
    53             'extra_cost' => array(
    54                 'title' => __('Valor adicional no frete', 'superfrete'),
    55                 'type' => 'price',
    56                 'description' => __('Valor extra a ser somado ao custo do frete', 'superfrete'),
    57                 'default' => '0',
    58                 'desc_tip' => true
    59             ),
    60             'extra_cost_type' => array(
    61                 'title' => __('Tipo de valor adicional', 'superfrete'),
    62                 'type' => 'select',
    63                 'description' => __('Escolha se o valor adicional será fixo (R$) ou percentual (%) sobre o frete original.', 'superfrete'),
    64                 'default' => 'fixed',
    65                 'options' => array(
    66                     'fixed' => __('Valor fixo (R$)', 'superfrete'),
    67                     'percent' => __('Porcentagem (%)', 'superfrete'),
    68                 ),
    69             ),
    70         );
    71         $this->enabled = $this->get_option('enabled');
    72         $this->free_shipping = $this->get_option('free_shipping');
    73         $this->title = $this->get_option('title');
    74         $this->extra_days = $this->get_option('extra_days', 0);
    75         $this->extra_cost = $this->get_option('extra_cost', 0);
    76         $this->extra_cost_type = $this->get_option('extra_cost_type', 'fixed');
     10class SuperFretePAC extends SuperFreteBase {
    7711
    78         add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
     12    public function __construct($instance_id = 0) {
     13        $this->id = 'superfrete_pac';
     14        $this->method_title = __('PAC SuperFrete');
     15        $this->method_description = __('Envia utilizando PAC');
     16       
     17        parent::__construct($instance_id);
    7918    }
     19
    8020    protected function get_service_id() {
    8121        return 1; // ID do PAC na API
    8222    }
    83 
    84 
    85     /**
    86      * Calcula o frete e retorna as opções disponíveis.
    87      */
    88      public function calculate_shipping($package = []) {
    89 
    90         if (!$this->enabled || empty($package['destination']['postcode'])) {
    91             return;
    92         }
    93 
    94         $cep_destino = $package['destination']['postcode'];
    95         $peso_total = 0;
    96 $produtos = [];
    97      $insurance_value = 0;
    98 foreach ($package['contents'] as $item) {
    99     $product = $item['data'];
    100    
    101    
    102    
    103    
    104    $insurance_value += $product->get_price() * $item['quantity'];
    105 $weight_unit = get_option('woocommerce_weight_unit');
    106 $dimension_unit = get_option('woocommerce_dimension_unit');
    107 
    108     $produtos[] = [
    109         'quantity' => $item['quantity'],
    110       'weight'   => ($weight_unit === 'g') ? floatval($product->get_weight()) / 1000 : floatval($product->get_weight()),
    111     'height'   => ($dimension_unit === 'm') ? floatval($product->get_height()) * 100 : floatval($product->get_height()),
    112     'width'    => ($dimension_unit === 'm') ? floatval($product->get_width()) * 100 : floatval($product->get_width()),
    113     'length'   => ($dimension_unit === 'm') ? floatval($product->get_length()) * 100 : floatval($product->get_length()),
    114 
    115     ];
    11623}
    117 
    118 $cep_origem = get_option('woocommerce_store_postcode');
    119 
    120 if (!$cep_origem) {
    121     return;
    122 }
    123 
    124 $payload = [
    125     'from' => ['postal_code' => $cep_origem],
    126     'to' => ['postal_code' => $cep_destino],
    127     'services' => "1",
    128     'options'=> [
    129       "own_hand"=>false,
    130       "receipt"=>false,
    131       "insurance_value"=> $insurance_value,
    132       "use_insurance_value"=>false
    133           ],
    134 
    135     'products' => $produtos,
    136 ];
    137 
    138 $request = new Request();
    139 
    140 
    141 $response = $request->call_superfrete_api('/api/v0/calculator', 'POST', $payload);
    142 
    143         if (!empty($response)) {
    144             foreach ($response as $frete) {
    145                 $prazo_total = $frete['delivery_time'] + $this->extra_days;
    146                 $text_dias = ($prazo_total <= 1) ? "dia útil" : "dias úteis";
    147 
    148                 if (!$frete['has_error'] && !isset($frete["error"])) {
    149                    $frete_custo = 0;
    150 
    151 if ($this->free_shipping !== 'yes') {
    152     $frete_base = floatval($frete['price']);
    153     if ($this->extra_cost_type === 'percent') {
    154         $frete_custo = $frete_base + ($frete_base * ($this->extra_cost / 100));
    155     } else {
    156         $frete_custo = $frete_base + $this->extra_cost;
    157     }
    158 }
    159 
    160                     $frete_desc = ($this->free_shipping === 'yes') ? "- Frete Grátis" : "";
    161 
    162                     $rate = [
    163                         'id' => $this->id . '_' . $frete['id'],
    164                         'label' => $frete['name'] . ' - Promocional - (' . $prazo_total . ' ' . $text_dias . ') ' . $frete_desc,
    165                         'cost' => $frete_custo
    166                     ];
    167                     $this->add_rate($rate);
    168                 }
    169             }
    170         }
    171     }
    172 
    173     public function process_admin_options() {
    174         parent::process_admin_options(); // Chama a função do WooCommerce para salvar
    175     }
    176 }
  • superfrete/trunk/app/Shipping/SuperFreteSEDEX.php

    r3289983 r3341621  
    33namespace SuperFrete_API\Shipping;
    44
    5 use SuperFrete_API\Http\Request;
    6 use SuperFrete_API\Helpers\Logger;
    7 
    85if (!defined('ABSPATH'))
    96    exit; // Segurança
    107
    11 class SuperFreteSEDEX extends \WC_Shipping_Method {
     8require_once plugin_dir_path(__FILE__) . 'SuperFreteBase.php';
    129
    13     public $free_shipping;
    14     public $extra_days;
    15     public $extra_cost;
    16     public $extra_cost_type;
     10class SuperFreteSEDEX extends SuperFreteBase {
    1711
    18     public function __construct($instance_id = 'superfrete_sedex') {
     12    public function __construct($instance_id = 0) {
    1913        $this->id = 'superfrete_sedex';
    20         $this->instance_id = absint($instance_id);
    21         $this->method_title = __('Sedex SuperFrete');
    22         $this->method_description = __('Envia utilizando Sedex');
    23         $this->supports = array(
    24             'shipping-zones',
    25             'instance-settings',
    26         );
    27         $this->instance_form_fields = array(
    28             'enabled' => array(
    29                 'title' => __('Ativar/Desativar'),
    30                 'type' => 'checkbox',
    31                 'label' => __('Ativar SuperFrete nas áreas de entrega', 'superfrete'),
    32                 'default' => 'yes',
    33             ),
    34             'title' => array(
    35                 'title' => __('Method Title'),
    36                 'type' => 'text',
    37                 'description' => __('Este título aparecerá no checkout', 'superfrete'),
    38                 'default' => __('Entrega via Sedex SuperFrete', 'superfrete'),
    39                 'desc_tip' => true
    40             ),
    41             'free_shipping' => array(
    42                 'title' => __('Frete Grátis', 'superfrete'),
    43                 'type' => 'checkbox',
    44                 'label' => __('Ativar frete grátis para este método', 'superfrete'),
    45             ),
    46             'extra_days' => array(
    47                 'title' => __('Dias extras no prazo', 'superfrete'),
    48                 'type' => 'number',
    49                 'description' => __('Dias adicionais ao prazo estimado pela SuperFrete', 'superfrete'),
    50                 'default' => 0,
    51                 'desc_tip' => true
    52             ),
    53             'extra_cost' => array(
    54                 'title' => __('Valor adicional no frete', 'superfrete'),
    55                 'type' => 'price',
    56                 'description' => __('Valor extra a ser somado ao custo do frete', 'superfrete'),
    57                 'default' => '0',
    58                 'desc_tip' => true
    59             ),
    60             'extra_cost_type' => array(
    61                 'title' => __('Tipo de valor adicional', 'superfrete'),
    62                 'type' => 'select',
    63                 'description' => __('Escolha se o valor adicional será fixo (R$) ou percentual (%) sobre o frete original.', 'superfrete'),
    64                 'default' => 'fixed',
    65                 'options' => array(
    66                     'fixed' => __('Valor fixo (R$)', 'superfrete'),
    67                     'percent' => __('Porcentagem (%)', 'superfrete'),
    68                 ),
    69             ),
    70         );
    71         $this->enabled = $this->get_option('enabled');
    72         $this->free_shipping = $this->get_option('free_shipping');
    73         $this->title = $this->get_option('title');
    74         $this->extra_days = $this->get_option('extra_days', 0);
    75         $this->extra_cost = $this->get_option('extra_cost', 0);
    76         $this->extra_cost_type = $this->get_option('extra_cost_type', 'fixed');
    77 
    78         add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
     14        $this->method_title = __('SEDEX SuperFrete');
     15        $this->method_description = __('Envia utilizando SEDEX');
     16       
     17        parent::__construct($instance_id);
    7918    }
    8019
    8120    protected function get_service_id() {
    82         return 2; // ID do Sedex na API
    83     }
    84 
    85     /**
    86      * Calcula o frete e retorna as opções disponíveis.
    87      */
    88     public function calculate_shipping($package = []) {
    89 
    90         if (!$this->enabled || empty($package['destination']['postcode'])) {
    91             return;
    92         }
    93 
    94         $cep_destino = $package['destination']['postcode'];
    95         $peso_total = 0;
    96         $produtos = [];
    97         $insurance_value = 0;
    98         foreach ($package['contents'] as $item) {
    99             $product = $item['data'];
    100 
    101             $insurance_value += $product->get_price() * $item['quantity'];
    102             $weight_unit = get_option('woocommerce_weight_unit');
    103             $dimension_unit = get_option('woocommerce_dimension_unit');
    104 
    105             $produtos[] = [
    106                 'quantity' => $item['quantity'],
    107                 'weight' => ($weight_unit === 'g') ? floatval($product->get_weight()) / 1000 : floatval($product->get_weight()),
    108                 'height' => ($dimension_unit === 'm') ? floatval($product->get_height()) * 100 : floatval($product->get_height()),
    109                 'width' => ($dimension_unit === 'm') ? floatval($product->get_width()) * 100 : floatval($product->get_width()),
    110                 'length' => ($dimension_unit === 'm') ? floatval($product->get_length()) * 100 : floatval($product->get_length()),
    111             ];
    112         }
    113 
    114         $cep_origem = get_option('woocommerce_store_postcode');
    115 
    116         if (!$cep_origem) {
    117             return;
    118         }
    119 
    120         $payload = [
    121             'from' => ['postal_code' => $cep_origem],
    122             'to' => ['postal_code' => $cep_destino],
    123             'services' => "2",
    124             'options' => [
    125                 "own_hand" => false,
    126                 "receipt" => false,
    127                 "insurance_value" => $insurance_value,
    128                 "use_insurance_value" => false
    129             ],
    130             'products' => $produtos,
    131         ];
    132 
    133         $request = new Request();
    134 
    135         $response = $request->call_superfrete_api('/api/v0/calculator', 'POST', $payload);
    136 
    137         if (!empty($response)) {
    138             foreach ($response as $frete) {
    139                 $prazo_total = $frete['delivery_time'] + $this->extra_days;
    140                 $text_dias = ($prazo_total <= 1) ? "dia útil" : "dias úteis";
    141 
    142                 if (!$frete['has_error'] && !isset($frete["error"])) {
    143                    $frete_custo = 0;
    144 
    145 if ($this->free_shipping !== 'yes') {
    146     $frete_base = floatval($frete['price']);
    147     if ($this->extra_cost_type === 'percent') {
    148         $frete_custo = $frete_base + ($frete_base * ($this->extra_cost / 100));
    149     } else {
    150         $frete_custo = $frete_base + $this->extra_cost;
     21        return 2; // ID do SEDEX na API
    15122    }
    15223}
    153 
    154                     $frete_desc = ($this->free_shipping === 'yes') ? "- Frete Grátis" : "";
    155 
    156                     $rate = [
    157                         'id' => $this->id . '_' . $frete['id'],
    158                         'label' => $frete['name'] . ' - Promocional - (' . $prazo_total . ' ' . $text_dias . ') ' . $frete_desc,
    159                         'cost' => $frete_custo
    160                     ];
    161                     $this->add_rate($rate);
    162                 }
    163             }
    164         }
    165     }
    166 
    167     public function process_admin_options() {
    168         parent::process_admin_options(); // Chama a função do WooCommerce para salvar
    169     }
    170 }
  • superfrete/trunk/app/Shipping/SuperFreteShipping.php

    r3289983 r3341621  
    55use SuperFrete_API\Http\Request;
    66use SuperFrete_API\Helpers\Logger;
     7use SuperFrete_API\Helpers\AddressHelper;
    78
    89if (!defined('ABSPATH'))
     
    1112class SuperFreteShipping extends \WC_Shipping_Method {
    1213
     14    private static $cache = [];
     15    private static $cache_duration = 1800; // 30 minutes cache for shipping rates
     16    private static $persistent_cache_key = 'superfrete_shipping_cache';
     17
    1318    public function __construct($instance_id = 0) {
    14         $this->id = 'superfrete';
     19        $this->id = 'superfrete_shipping';
    1520        $this->instance_id = absint($instance_id);
    16         $this->method_title = __('SuperFrete', 'superfrete');
    17         $this->method_description = __('Método de envio via SuperFrete API.', 'superfrete');
    18         $this->supports = ['shipping-zones', 'instance-settings'];
    19 
    20         $this->init();
    21     }
    22 
    23     /**
    24      * Inicializa as configurações do método de envio.
    25      */
    26     public function init() {
    27         // Carrega as configurações do método de envio
    28         $this->init_form_fields();
    29         $this->init_settings();
    30 
    31         $this->enabled = $this->get_option('enabled');
    32         $this->title = $this->get_option('title');
    33     }
    34 
    35     /**
    36      * Define os campos do método de envio no painel de administração.
    37      */
    38     public function init_form_fields() {
    39         $this->form_fields = [
    40             'enabled' => [
    41                 'title' => __('Ativar/Desativar', 'superfrete'),
     21        $this->method_title = __('SuperFrete Shipping');
     22        $this->method_description = __('Calcula frete usando a API da SuperFrete');
     23        $this->supports = array(
     24            'shipping-zones',
     25            'instance-settings',
     26        );
     27
     28        $this->instance_form_fields = array(
     29            'enabled' => array(
     30                'title' => __('Ativar/Desativar'),
    4231                'type' => 'checkbox',
    4332                'label' => __('Ativar SuperFrete nas áreas de entrega', 'superfrete'),
    4433                'default' => 'yes',
    45             ],
    46             'title' => [
    47                 'title' => __('Título', 'superfrete'),
     34            ),
     35            'title' => array(
     36                'title' => __('Título do Método'),
    4837                'type' => 'text',
    4938                'description' => __('Este título aparecerá no checkout', 'superfrete'),
    50                 'default' => __('Entrega via SuperFrete', 'superfrete'),
    51                 'desc_tip' => true,
    52             ],
    53         ];
    54     }
    55 
    56     /**
    57      * Calcula o frete e retorna as opções disponíveis.
     39                'default' => __('SuperFrete', 'superfrete'),
     40                'desc_tip' => true
     41            ),
     42            'services' => array(
     43                'title' => __('Serviços Ativos'),
     44                'type' => 'multiselect',
     45                'description' => __('Selecione quais serviços da SuperFrete devem estar disponíveis', 'superfrete'),
     46                'default' => array('1', '2', '17'),
     47                'options' => array(
     48                    '1' => __('PAC', 'superfrete'),
     49                    '2' => __('SEDEX', 'superfrete'),
     50                    '3' => __('Jadlog', 'superfrete'),
     51                    '17' => __('Mini Envios', 'superfrete'),
     52                    '31' => __('Loggi', 'superfrete'),
     53                ),
     54                'desc_tip' => true
     55            ),
     56           
     57            // Global settings section
     58            'global_settings' => array(
     59                'title' => __('Configurações Globais (aplicam-se a todos os serviços se não configurados individualmente)', 'superfrete'),
     60                'type' => 'title',
     61            ),
     62            'extra_days' => array(
     63                'title' => __('Dias extras no prazo'),
     64                'type' => 'number',
     65                'description' => __('Dias adicionais ao prazo estimado pela SuperFrete'),
     66                'default' => 0,
     67                'desc_tip' => true
     68            ),
     69            'extra_cost' => array(
     70                'title' => __('Valor adicional no frete'),
     71                'type' => 'price',
     72                'description' => __('Valor extra a ser somado ao custo do frete'),
     73                'default' => '0',
     74                'desc_tip' => true
     75            ),
     76            'extra_cost_type' => array(
     77                'title' => __('Tipo de valor adicional'),
     78                'type' => 'select',
     79                'description' => __('Escolha se o valor adicional será fixo (R$) ou percentual (%) sobre o frete original.'),
     80                'default' => 'fixed',
     81                'options' => array(
     82                    'fixed' => __('Valor fixo (R$)', 'superfrete'),
     83                    'percent' => __('Porcentagem (%)', 'superfrete'),
     84                ),
     85            ),
     86           
     87            // PAC individual settings
     88            'pac_settings' => array(
     89                'title' => __('Configurações PAC', 'superfrete'),
     90                'type' => 'title',
     91                'description' => __('Configurações específicas para o serviço PAC (deixe vazio para usar configurações globais)', 'superfrete'),
     92            ),
     93            'pac_free_shipping' => array(
     94                'title' => __('Frete Grátis PAC'),
     95                'type' => 'checkbox',
     96                'label' => __('Ativar frete grátis para PAC', 'superfrete'),
     97                'default' => 'no',
     98            ),
     99            'pac_extra_days' => array(
     100                'title' => __('Dias extras PAC'),
     101                'type' => 'number',
     102                'description' => __('Dias adicionais específicos para PAC (vazio = usar global)', 'superfrete'),
     103                'default' => '',
     104                'desc_tip' => true
     105            ),
     106            'pac_extra_cost' => array(
     107                'title' => __('Valor adicional PAC'),
     108                'type' => 'price',
     109                'description' => __('Valor extra específico para PAC (vazio = usar global)', 'superfrete'),
     110                'default' => '',
     111                'desc_tip' => true
     112            ),
     113            'pac_extra_cost_type' => array(
     114                'title' => __('Tipo de valor adicional PAC'),
     115                'type' => 'select',
     116                'default' => '',
     117                'options' => array(
     118                    '' => __('Usar configuração global', 'superfrete'),
     119                    'fixed' => __('Valor fixo (R$)', 'superfrete'),
     120                    'percent' => __('Porcentagem (%)', 'superfrete'),
     121                ),
     122            ),
     123           
     124            // SEDEX individual settings
     125            'sedex_settings' => array(
     126                'title' => __('Configurações SEDEX', 'superfrete'),
     127                'type' => 'title',
     128                'description' => __('Configurações específicas para o serviço SEDEX (deixe vazio para usar configurações globais)', 'superfrete'),
     129            ),
     130            'sedex_free_shipping' => array(
     131                'title' => __('Frete Grátis SEDEX'),
     132                'type' => 'checkbox',
     133                'label' => __('Ativar frete grátis para SEDEX', 'superfrete'),
     134                'default' => 'no',
     135            ),
     136            'sedex_extra_days' => array(
     137                'title' => __('Dias extras SEDEX'),
     138                'type' => 'number',
     139                'description' => __('Dias adicionais específicos para SEDEX (vazio = usar global)', 'superfrete'),
     140                'default' => '',
     141                'desc_tip' => true
     142            ),
     143            'sedex_extra_cost' => array(
     144                'title' => __('Valor adicional SEDEX'),
     145                'type' => 'price',
     146                'description' => __('Valor extra específico para SEDEX (vazio = usar global)', 'superfrete'),
     147                'default' => '',
     148                'desc_tip' => true
     149            ),
     150            'sedex_extra_cost_type' => array(
     151                'title' => __('Tipo de valor adicional SEDEX'),
     152                'type' => 'select',
     153                'default' => '',
     154                'options' => array(
     155                    '' => __('Usar configuração global', 'superfrete'),
     156                    'fixed' => __('Valor fixo (R$)', 'superfrete'),
     157                    'percent' => __('Porcentagem (%)', 'superfrete'),
     158                ),
     159            ),
     160           
     161            // Jadlog individual settings
     162            'jadlog_settings' => array(
     163                'title' => __('Configurações Jadlog', 'superfrete'),
     164                'type' => 'title',
     165                'description' => __('Configurações específicas para o serviço Jadlog (deixe vazio para usar configurações globais)', 'superfrete'),
     166            ),
     167            'jadlog_free_shipping' => array(
     168                'title' => __('Frete Grátis Jadlog'),
     169                'type' => 'checkbox',
     170                'label' => __('Ativar frete grátis para Jadlog', 'superfrete'),
     171                'default' => 'no',
     172            ),
     173            'jadlog_extra_days' => array(
     174                'title' => __('Dias extras Jadlog'),
     175                'type' => 'number',
     176                'description' => __('Dias adicionais específicos para Jadlog (vazio = usar global)', 'superfrete'),
     177                'default' => '',
     178                'desc_tip' => true
     179            ),
     180            'jadlog_extra_cost' => array(
     181                'title' => __('Valor adicional Jadlog'),
     182                'type' => 'price',
     183                'description' => __('Valor extra específico para Jadlog (vazio = usar global)', 'superfrete'),
     184                'default' => '',
     185                'desc_tip' => true
     186            ),
     187            'jadlog_extra_cost_type' => array(
     188                'title' => __('Tipo de valor adicional Jadlog'),
     189                'type' => 'select',
     190                'default' => '',
     191                'options' => array(
     192                    '' => __('Usar configuração global', 'superfrete'),
     193                    'fixed' => __('Valor fixo (R$)', 'superfrete'),
     194                    'percent' => __('Porcentagem (%)', 'superfrete'),
     195                ),
     196            ),
     197           
     198            // Mini Envios individual settings
     199            'mini_envios_settings' => array(
     200                'title' => __('Configurações Mini Envios', 'superfrete'),
     201                'type' => 'title',
     202                'description' => __('Configurações específicas para o serviço Mini Envios (deixe vazio para usar configurações globais)', 'superfrete'),
     203            ),
     204            'mini_envios_free_shipping' => array(
     205                'title' => __('Frete Grátis Mini Envios'),
     206                'type' => 'checkbox',
     207                'label' => __('Ativar frete grátis para Mini Envios', 'superfrete'),
     208                'default' => 'no',
     209            ),
     210            'mini_envios_extra_days' => array(
     211                'title' => __('Dias extras Mini Envios'),
     212                'type' => 'number',
     213                'description' => __('Dias adicionais específicos para Mini Envios (vazio = usar global)', 'superfrete'),
     214                'default' => '',
     215                'desc_tip' => true
     216            ),
     217            'mini_envios_extra_cost' => array(
     218                'title' => __('Valor adicional Mini Envios'),
     219                'type' => 'price',
     220                'description' => __('Valor extra específico para Mini Envios (vazio = usar global)', 'superfrete'),
     221                'default' => '',
     222                'desc_tip' => true
     223            ),
     224            'mini_envios_extra_cost_type' => array(
     225                'title' => __('Tipo de valor adicional Mini Envios'),
     226                'type' => 'select',
     227                'default' => '',
     228                'options' => array(
     229                    '' => __('Usar configuração global', 'superfrete'),
     230                    'fixed' => __('Valor fixo (R$)', 'superfrete'),
     231                    'percent' => __('Porcentagem (%)', 'superfrete'),
     232                ),
     233            ),
     234           
     235            // Loggi individual settings
     236            'loggi_settings' => array(
     237                'title' => __('Configurações Loggi', 'superfrete'),
     238                'type' => 'title',
     239                'description' => __('Configurações específicas para o serviço Loggi (deixe vazio para usar configurações globais)', 'superfrete'),
     240            ),
     241            'loggi_free_shipping' => array(
     242                'title' => __('Frete Grátis Loggi'),
     243                'type' => 'checkbox',
     244                'label' => __('Ativar frete grátis para Loggi', 'superfrete'),
     245                'default' => 'no',
     246            ),
     247            'loggi_extra_days' => array(
     248                'title' => __('Dias extras Loggi'),
     249                'type' => 'number',
     250                'description' => __('Dias adicionais específicos para Loggi (vazio = usar global)', 'superfrete'),
     251                'default' => '',
     252                'desc_tip' => true
     253            ),
     254            'loggi_extra_cost' => array(
     255                'title' => __('Valor adicional Loggi'),
     256                'type' => 'price',
     257                'description' => __('Valor extra específico para Loggi (vazio = usar global)', 'superfrete'),
     258                'default' => '',
     259                'desc_tip' => true
     260            ),
     261            'loggi_extra_cost_type' => array(
     262                'title' => __('Tipo de valor adicional Loggi'),
     263                'type' => 'select',
     264                'default' => '',
     265                'options' => array(
     266                    '' => __('Usar configuração global', 'superfrete'),
     267                    'fixed' => __('Valor fixo (R$)', 'superfrete'),
     268                    'percent' => __('Porcentagem (%)', 'superfrete'),
     269                ),
     270            ),
     271        );
     272
     273        $this->enabled = $this->get_option('enabled');
     274        $this->title = $this->get_option('title');
     275
     276        add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
     277    }
     278
     279    /**
     280     * Calculate shipping rates for the package
    58281     */
    59282    public function calculate_shipping($package = []) {
     
    62285        }
    63286
     287        // Only calculate shipping in specific contexts to avoid unnecessary API calls
     288        if (!$this->should_calculate_shipping()) {
     289            Logger::log('SuperFrete', 'Skipping shipping calculation - not in required context');
     290            return;
     291        }
     292
    64293        $cep_destino = $package['destination']['postcode'];
    65         $peso_total = 0;
    66         $dimensoes = ['height' => 2, 'width' => 11, 'length' => 16, 'weight' => 0.3];
    67 
     294        $cep_origem = get_option('woocommerce_store_postcode');
     295
     296        if (!$cep_origem) {
     297            Logger::log('SuperFrete', 'Origin postcode not configured');
     298            return;
     299        }
     300
     301        // Create cache key based on package contents and destination
     302        $cache_key = $this->generate_cache_key($package);
     303        $destination_cep = $package['destination']['postcode'];
     304       
     305        Logger::log('SuperFrete', 'Calculating shipping for CEP: ' . $destination_cep . ' with cache key: ' . $cache_key);
     306       
     307        // Check cache first (both memory and persistent)
     308        $response = $this->get_cached_response($cache_key);
     309       
     310        if ($response === false) {
     311            Logger::log('SuperFrete', 'Cache MISS for CEP: ' . $destination_cep . ' - making API call');
     312            // Make API call
     313            $response = $this->call_superfrete_api($package);
     314           
     315            // Cache the response if successful
     316            if ($response) {
     317                $this->cache_response($cache_key, $response);
     318                Logger::log('SuperFrete', 'Cached new shipping rates for CEP: ' . $destination_cep . ' with key: ' . $cache_key);
     319            }
     320        } else {
     321            Logger::log('SuperFrete', 'Cache HIT for CEP: ' . $destination_cep . ' - using cached rates with key: ' . $cache_key);
     322        }
     323
     324        if (!empty($response)) {
     325            $this->process_shipping_response($response);
     326        }
     327    }
     328
     329    /**
     330     * Generate cache key for the shipping calculation
     331     */
     332    private function generate_cache_key($package) {
     333        // Clean and normalize postal codes
     334        $destination_postcode = preg_replace('/[^0-9]/', '', $package['destination']['postcode']);
     335        $origin_postcode = preg_replace('/[^0-9]/', '', get_option('woocommerce_store_postcode'));
     336       
     337        $key_data = [
     338            'destination' => $destination_postcode,
     339            'origin' => $origin_postcode,
     340            'services' => $this->get_option('services', array('1', '2', '17')),
     341            'contents' => [],
     342            'version' => '2.0' // Increment this to invalidate all cache when logic changes
     343        ];
     344
     345        // Sort contents by product ID for consistent cache keys
     346        $contents = [];
    68347        foreach ($package['contents'] as $item) {
    69348            $product = $item['data'];
    70             $peso_total += $product->get_weight() * $item['quantity'];
    71             $dimensoes['height'] = max($dimensoes['height'], $product->get_height());
    72             $dimensoes['width'] = max($dimensoes['width'], $product->get_width());
    73             $dimensoes['length'] += $product->get_length();
    74         }
    75 
     349            $contents[] = [
     350                'id' => $product->get_id(),
     351                'quantity' => $item['quantity'],
     352                'weight' => floatval($product->get_weight()),
     353                'dimensions' => [
     354                    'height' => floatval($product->get_height()),
     355                    'width' => floatval($product->get_width()),
     356                    'length' => floatval($product->get_length()),
     357                ]
     358            ];
     359        }
     360       
     361        // Sort by product ID for consistent cache keys regardless of order
     362        usort($contents, function($a, $b) {
     363            return $a['id'] <=> $b['id'];
     364        });
     365       
     366        $key_data['contents'] = $contents;
     367
     368        return 'superfrete_v2_' . md5(serialize($key_data));
     369    }
     370
     371    /**
     372     * Call SuperFrete API with consolidated request
     373     */
     374    private function call_superfrete_api($package) {
     375        $cep_destino = $package['destination']['postcode'];
    76376        $cep_origem = get_option('woocommerce_store_postcode');
    77 
    78         if (!$cep_origem) {
    79             return;
    80         }
     377       
     378        $produtos = [];
     379        $insurance_value = 0;
     380       
     381        foreach ($package['contents'] as $item) {
     382            $product = $item['data'];
     383            $insurance_value += $product->get_price() * $item['quantity'];
     384           
     385            $weight_unit = get_option('woocommerce_weight_unit');
     386            $dimension_unit = get_option('woocommerce_dimension_unit');
     387
     388            $produtos[] = [
     389                'quantity' => $item['quantity'],
     390                'weight' => ($weight_unit === 'g') ? floatval($product->get_weight()) / 1000 : floatval($product->get_weight()),
     391                'height' => ($dimension_unit === 'm') ? floatval($product->get_height()) * 100 : floatval($product->get_height()),
     392                'width' => ($dimension_unit === 'm') ? floatval($product->get_width()) * 100 : floatval($product->get_width()),
     393                'length' => ($dimension_unit === 'm') ? floatval($product->get_length()) * 100 : floatval($product->get_length()),
     394            ];
     395        }
     396
     397        // Get active services from settings
     398        $active_services = $this->get_option('services', array('1', '2', '17'));
     399        $services_string = implode(',', $active_services);
    81400
    82401        $payload = [
    83402            'from' => ['postal_code' => $cep_origem],
    84403            'to' => ['postal_code' => $cep_destino],
    85             'services' => "1,2,17",
    86             'package' => $dimensoes,
     404            'services' => $services_string,
     405            'options' => [
     406                "own_hand" => false,
     407                "receipt" => false,
     408                "insurance_value" => $insurance_value,
     409                "use_insurance_value" => false
     410            ],
     411            'products' => $produtos,
    87412        ];
     413
     414        Logger::log('SuperFrete', 'Making consolidated API call for services: ' . $services_string . ' to CEP: ' . $cep_destino);
     415        Logger::log('SuperFrete', 'API payload: ' . wp_json_encode($payload));
     416       
    88417        $request = new Request();
    89418        $response = $request->call_superfrete_api('/api/v0/calculator', 'POST', $payload);
    90 
    91         if (!empty($response)) {
    92             foreach ($response as $frete) {
    93                 if (!$frete['has_error']) {
    94                     $rate = [
    95                         'id' => $this->id . '_' . $frete['id'],
    96                         'label' => $frete['name'] . ' - (' . $frete['delivery_time'] . ' dias úteis)',
    97                         'cost' => floatval($frete['price']),
    98                     ];
    99                     $this->add_rate($rate);
     419       
     420        if ($response === false) {
     421            Logger::log('SuperFrete', 'API call failed - no response received');
     422            return false;
     423        }
     424       
     425        if (empty($response)) {
     426            Logger::log('SuperFrete', 'API call returned empty response');
     427            return false;
     428        }
     429       
     430        Logger::log('SuperFrete', 'API response received: ' . wp_json_encode($response));
     431        return $response;
     432    }
     433
     434    /**
     435     * Get service-specific settings or fall back to global settings
     436     */
     437    private function get_service_settings($service_id) {
     438        $service_map = [
     439            '1' => 'pac',
     440            '2' => 'sedex',
     441            '3' => 'jadlog',
     442            '17' => 'mini_envios',
     443            '31' => 'loggi',
     444        ];
     445       
     446        $service_prefix = isset($service_map[$service_id]) ? $service_map[$service_id] : null;
     447       
     448        if (!$service_prefix) {
     449            // Unknown service, use global settings
     450            return [
     451                'free_shipping' => false,
     452                'extra_days' => $this->get_option('extra_days', 0),
     453                'extra_cost' => $this->get_option('extra_cost', 0),
     454                'extra_cost_type' => $this->get_option('extra_cost_type', 'fixed'),
     455            ];
     456        }
     457       
     458        // Get service-specific settings with fallback to global
     459        $free_shipping = $this->get_option($service_prefix . '_free_shipping', 'no') === 'yes';
     460       
     461        $extra_days = $this->get_option($service_prefix . '_extra_days', '');
     462        if ($extra_days === '') {
     463            $extra_days = $this->get_option('extra_days', 0);
     464        }
     465       
     466        $extra_cost = $this->get_option($service_prefix . '_extra_cost', '');
     467        if ($extra_cost === '') {
     468            $extra_cost = $this->get_option('extra_cost', 0);
     469        }
     470       
     471        $extra_cost_type = $this->get_option($service_prefix . '_extra_cost_type', '');
     472        if ($extra_cost_type === '') {
     473            $extra_cost_type = $this->get_option('extra_cost_type', 'fixed');
     474        }
     475       
     476        return [
     477            'free_shipping' => $free_shipping,
     478            'extra_days' => intval($extra_days),
     479            'extra_cost' => floatval($extra_cost),
     480            'extra_cost_type' => $extra_cost_type,
     481        ];
     482    }
     483
     484    /**
     485     * Process the API response and add shipping rates
     486     */
     487    private function process_shipping_response($response) {
     488        if (!is_array($response)) {
     489            Logger::log('SuperFrete', 'Invalid response format - expected array, got: ' . gettype($response));
     490            return;
     491        }
     492
     493        if (empty($response)) {
     494            Logger::log('SuperFrete', 'Empty response array - no shipping options available');
     495            return;
     496        }
     497
     498        Logger::log('SuperFrete', 'Processing ' . count($response) . ' shipping options');
     499
     500        $rates_added = 0;
     501
     502        foreach ($response as $index => $frete) {
     503            Logger::log('SuperFrete', 'Processing shipping option ' . $index . ': ' . wp_json_encode($frete));
     504
     505            // Check for errors in this shipping option
     506            if (isset($frete['has_error']) && $frete['has_error']) {
     507                Logger::log('SuperFrete', 'Shipping option ' . $index . ' has error flag set');
     508                continue;
     509            }
     510
     511            if (isset($frete['error'])) {
     512                Logger::log('SuperFrete', 'Shipping option ' . $index . ' has error: ' . wp_json_encode($frete['error']));
     513                continue;
     514            }
     515
     516            // Validate required fields
     517            if (!isset($frete['id']) || !isset($frete['name']) || !isset($frete['price']) || !isset($frete['delivery_time'])) {
     518                Logger::log('SuperFrete', 'Shipping option ' . $index . ' missing required fields: ' . wp_json_encode(array_keys($frete)));
     519                continue;
     520            }
     521
     522            // Get service-specific settings
     523            $service_settings = $this->get_service_settings($frete['id']);
     524           
     525            // Skip if free shipping is enabled for this service
     526            if ($service_settings['free_shipping']) {
     527                $frete_custo = 0;
     528                $frete_desc = " - Frete Grátis";
     529            } else {
     530                // Calculate cost with extra fees
     531                $frete_base = floatval($frete['price']);
     532                if ($service_settings['extra_cost_type'] === 'percent') {
     533                    $frete_custo = $frete_base + ($frete_base * ($service_settings['extra_cost'] / 100));
     534                } else {
     535                    $frete_custo = $frete_base + $service_settings['extra_cost'];
    100536                }
     537                $frete_desc = "";
    101538            }
    102         }
     539
     540            $prazo_total = intval($frete['delivery_time']) + $service_settings['extra_days'];
     541            $text_dias = ($prazo_total <= 1) ? "dia útil" : "dias úteis";
     542
     543            $rate = [
     544                'id' => $this->id . '_' . $frete['id'],
     545                'label' => $frete['name'] . ' - (' . $prazo_total . ' ' . $text_dias . ')' . $frete_desc,
     546                'cost' => $frete_custo,
     547                'meta_data' => [
     548                    'service_id' => $frete['id'],
     549                    'service_name' => $frete['name'],
     550                    'delivery_time' => $prazo_total,
     551                    'original_price' => floatval($frete['price']),
     552                    'free_shipping' => $service_settings['free_shipping'],
     553                ]
     554            ];
     555           
     556            $this->add_rate($rate);
     557            $rates_added++;
     558            Logger::log('SuperFrete', 'Added shipping rate: ' . $frete['name'] . ' - R$ ' . number_format($frete_custo, 2, ',', '.') . ' (' . $prazo_total . ' ' . $text_dias . ')' . $frete_desc);
     559        }
     560
     561        Logger::log('SuperFrete', 'Total shipping rates added: ' . $rates_added);
     562    }
     563
     564    /**
     565     * Determine if we should calculate shipping in the current context
     566     */
     567    private function should_calculate_shipping() {
     568        // Always calculate if we're in admin (for testing)
     569        /* if (is_admin()) {
     570            return true;
     571        } */
     572
     573        // Always calculate on cart and checkout pages
     574        if (is_cart() || is_checkout()) {
     575            return true;
     576        }
     577
     578        // Check if this is a WooCommerce Store API request (block-based cart/checkout)
     579        if (defined('REST_REQUEST') && REST_REQUEST) {
     580            $request_uri = $_SERVER['REQUEST_URI'] ?? '';
     581           
     582            // Allow shipping calculations for WooCommerce Store API endpoints
     583            if (strpos($request_uri, '/wc/store/') !== false) {
     584                Logger::log('SuperFrete', 'Allowing shipping calculation for WooCommerce Store API: ' . $request_uri);
     585                return true;
     586            }
     587        }
     588
     589        // Calculate for our specific product shipping calculator AJAX
     590        if (wp_doing_ajax()) {
     591            $action = $_REQUEST['action'] ?? '';
     592           
     593            // Our specific shipping calculator
     594            if ($action === 'superfrete_cal_shipping') {
     595                return true;
     596            }
     597           
     598            // WooCommerce cart/checkout AJAX actions
     599            if (in_array($action, [
     600                'woocommerce_update_shipping_method',
     601                'woocommerce_checkout',
     602                'woocommerce_update_order_review',
     603                'woocommerce_apply_coupon',
     604                'woocommerce_remove_coupon'
     605            ])) {
     606                return true;
     607            }
     608           
     609            // Skip for other AJAX actions (like add to cart)
     610            Logger::log('SuperFrete', 'Skipping shipping calculation for AJAX action: ' . $action);
     611            return false;
     612        }
     613
     614        // Calculate for our specific product shipping calculator
     615        if (isset($_POST['superfrete_nonce']) && wp_verify_nonce($_POST['superfrete_nonce'], 'superfrete_nonce')) {
     616            return true;
     617        }
     618
     619        // Skip calculation for all other contexts (including product pages, add to cart, etc.)
     620        $context = 'unknown';
     621        if (is_product()) $context = 'product page';
     622        if (is_shop()) $context = 'shop page';
     623        if (is_home()) $context = 'home page';
     624       
     625        Logger::log('SuperFrete', 'Skipping shipping calculation - context: ' . $context);
     626        return false;
     627    }
     628
     629    /**
     630     * Get cached response from memory or persistent storage
     631     */
     632    private function get_cached_response($cache_key) {
     633        // First check memory cache (fastest)
     634        if (isset(self::$cache[$cache_key]) &&
     635            (time() - self::$cache[$cache_key]['timestamp']) < self::$cache_duration) {
     636            return self::$cache[$cache_key]['data'];
     637        }
     638       
     639        // Then check WordPress transients (persistent across requests)
     640        $transient_key = self::$persistent_cache_key . '_' . md5($cache_key);
     641        $cached_data = get_transient($transient_key);
     642       
     643        if ($cached_data !== false) {
     644            // Store in memory cache for subsequent calls in this request
     645            self::$cache[$cache_key] = [
     646                'data' => $cached_data,
     647                'timestamp' => time()
     648            ];
     649            return $cached_data;
     650        }
     651       
     652        return false;
     653    }
     654   
     655    /**
     656     * Cache response in both memory and persistent storage
     657     */
     658    private function cache_response($cache_key, $response) {
     659        // Store in memory cache
     660        self::$cache[$cache_key] = [
     661            'data' => $response,
     662            'timestamp' => time()
     663        ];
     664       
     665        // Store in WordPress transients for persistence
     666        $transient_key = self::$persistent_cache_key . '_' . md5($cache_key);
     667        set_transient($transient_key, $response, self::$cache_duration);
     668    }
     669
     670    /**
     671     * Clear cache when needed
     672     */
     673    public static function clear_cache() {
     674        // Clear memory cache
     675        self::$cache = [];
     676       
     677        // Clear persistent cache - we need to delete all transients with our prefix
     678        global $wpdb;
     679        $wpdb->query(
     680            $wpdb->prepare(
     681                "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
     682                '_transient_' . self::$persistent_cache_key . '_%'
     683            )
     684        );
     685        $wpdb->query(
     686            $wpdb->prepare(
     687                "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
     688                '_transient_timeout_' . self::$persistent_cache_key . '_%'
     689            )
     690        );
     691       
     692        Logger::log('SuperFrete', 'All shipping cache cleared (memory + persistent)');
     693    }
     694
     695    public function process_admin_options() {
     696        parent::process_admin_options();
     697        // Clear cache when settings are updated
     698        self::clear_cache();
     699        Logger::log('SuperFrete', 'Shipping settings updated - cache cleared');
     700    }
     701   
     702    /**
     703     * Get cache statistics for debugging
     704     */
     705    public static function get_cache_stats() {
     706        global $wpdb;
     707       
     708        $transient_count = $wpdb->get_var(
     709            $wpdb->prepare(
     710                "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s",
     711                '_transient_' . self::$persistent_cache_key . '_%'
     712            )
     713        );
     714       
     715        return [
     716            'memory_cache_entries' => count(self::$cache),
     717            'persistent_cache_entries' => intval($transient_count),
     718            'cache_duration_minutes' => self::$cache_duration / 60,
     719        ];
    103720    }
    104721}
  • superfrete/trunk/assets/scripts/superfrete-calculator.js

    r3289983 r3341621  
    11(function ($) {
    22    'use strict';
     3
     4    // Polyfill to get first array key in old browsers
     5    if (!Array.prototype.hasOwnProperty('flat')) {
     6        Object.defineProperty(Array.prototype, 'flat', {
     7            value: function (depth = 1) {
     8                return this.reduce(function (flat, toFlatten) {
     9                    return flat.concat((Array.isArray(toFlatten) && (depth > 1)) ? toFlatten.flat(depth - 1) : toFlatten);
     10                }, []);
     11            }
     12        });
     13    }
    314
    415    function managingCalculator() {
     
    2637            this.variationChange();
    2738
    28            
     39            // Clear empty alert container on page load
     40            jQuery(".superfrete-alert-container:empty").hide();
     41
     42            // Initialize CEP input masking and auto-submit
     43            this.initCEPMasking();
     44
     45            // No need for recalculate button handler - form is always visible
     46            // CEP input will auto-recalculate when changed
    2947        }
    3048
    3149        this.cal_init = function(){
    32             this.calculatorOpen();
    3350            this.submitDetect();
    34             if(!window?.superfrete_autoloading_done){
    35                 this.onloadShippingMethod(true);
    36             }
    3751            this.autoSelectCountry();
     52           
     53            // Check if CEP is pre-populated and calculate automatically
     54            var cepInput = jQuery('#calc_shipping_postcode');
     55            if (cepInput.length > 0) {
     56                var cepValue = cepInput.val().replace(/\D/g, '');
     57                if (cepValue.length === 8) {
     58                    // CEP is pre-populated with valid value, calculate freight
     59                    var parent = this;
     60                    // Store the pre-populated CEP to prevent duplicate calculations
     61                    cepInput.data('last-calculated-cep', cepValue);
     62                    setTimeout(function() {
     63                        jQuery("#superfrete-status-message p").text('🔄 Calculando frete automaticamente...');
     64                        parent.onloadShippingMethod();
     65                    }, 500); // Small delay to ensure page is fully loaded
     66                }
     67            }
    3868        }
    3969
     
    86116                        }else{
    87117                            parent.showCalculator();
    88                             window.superfrete_autoloading_done = 1;
    89118                            parent.setVariation(data);
    90119                            parent.noVariationSelectedMessage(false);
     
    114143
    115144        this.hideCalculator = function () {
    116             jQuery(".superfrete-container").fadeOut();
     145            // Calculator is always visible now, no need to hide
    117146        }
    118147
    119148        this.showCalculator = function () {
    120             jQuery(".superfrete-container").fadeIn();
     149            // Calculator is always visible now, no need to show
    121150        }
    122151
     
    128157            }
    129158            jQuery(".superfrete-woocommerce-shipping-calculator input[name='variation_id']").val(var_id);
    130             this.onloadShippingMethod(true);
     159            // REMOVED: Automatic calculation on variation change
     160            // Only calculate when user explicitly requests it
    131161        }
    132162
     
    134164            var parent = this;
    135165            jQuery(document).on("submit", "form.superfrete-woocommerce-shipping-calculator", { parent: parent }, parent.shipping_calculator_submit);
    136         }
    137 
    138         this.calculatorOpen = function () {
    139             jQuery(document).on('click', '.superfrete-shipping-calculator-button', function (e) {
    140                 e.preventDefault();
    141                 jQuery('.superfrete-shipping-calculator-form').toggle();
    142                 jQuery(document).trigger('superfrete_calculator_button_clicker');
    143             });
    144166        }
    145167
     
    164186            if(this.form_present() == false) return;
    165187           
    166             var e = jQuery('form.superfrete-woocommerce-shipping-calculator:visible').first();
     188            var e = jQuery('form.superfrete-woocommerce-shipping-calculator').first();
    167189            var parent = this;
    168190            if (jQuery("#superfrete-variation-id").length && jQuery("#superfrete-variation-id").val() == 0) {
     
    188210             * with this one ajax request is reduced when auto loading is set to off
    189211             */
    190            
    191            
    192 
    193212            jQuery.ajax({
    194213                type: e.attr("method"),
     
    197216                dataType: "json",
    198217                success: function (t) {                 
    199                        
    200                    
    201 
    202                     jQuery("#superfrete-alert-container, .superfrete-alert-container").html(t.shipping_methods);
    203                     jQuery("#superfrete-error, .superfrete-error").html(t.error);
     218                    // Output performance logs to console if available
     219                    if (t.performance_log) {
     220                        console.group("SuperFrete Performance Logs");
     221                        console.log("Total execution time: " + t.performance_log.total_time);
     222                       
     223                        // Sort steps by execution time (descending)
     224                        var steps = [];
     225                        for (var step in t.performance_log.steps) {
     226                            steps.push({
     227                                name: step,
     228                                time: parseFloat(t.performance_log.steps[step])
     229                            });
     230                        }
     231                       
     232                        steps.sort(function(a, b) {
     233                            return b.time - a.time;
     234                        });
     235                       
     236                        console.table(steps);
     237                       
     238                        // Show shipping method timings if available
     239                        if (t.performance_log.method_times) {
     240                            console.group("Shipping Method Timings");
     241                           
     242                            var methodTimes = [];
     243                            for (var method in t.performance_log.method_times) {
     244                                methodTimes.push({
     245                                    method: method,
     246                                    time: parseFloat(t.performance_log.method_times[method])
     247                                });
     248                            }
     249                           
     250                            methodTimes.sort(function(a, b) {
     251                                return b.time - a.time;
     252                            });
     253                           
     254                            console.table(methodTimes);
     255                            console.groupEnd();
     256                        }
     257                       
     258                        // Show HTTP API stats if available
     259                        if (t.performance_log.http_api) {
     260                            console.group("HTTP API Requests");
     261                            console.log("Total requests: " + t.performance_log.http_api.total_requests);
     262                            console.log("Total time: " + t.performance_log.http_api.total_time);
     263                            console.log("Average time: " + t.performance_log.http_api.average_time);
     264                           
     265                            if (t.performance_log.http_api.slow_requests) {
     266                                console.group("Slow Requests (>500ms)");
     267                                console.table(t.performance_log.http_api.slow_requests);
     268                                console.groupEnd();
     269                            }
     270                           
     271                            console.groupEnd();
     272                        }
     273                       
     274                        // Show shipping methods info
     275                        if (t.performance_log.shipping_methods) {
     276                            console.group("Shipping Methods");
     277                            console.table(t.performance_log.shipping_methods);
     278                            console.groupEnd();
     279                        }
     280                       
     281                        // Show slow methods if any
     282                        if (t.performance_log.slow_methods) {
     283                            console.group("⚠️ Slow Shipping Methods");
     284                            console.table(t.performance_log.slow_methods);
     285                            console.groupEnd();
     286                        }
     287                       
     288                        console.groupEnd();
     289                    }
     290                   
     291                    // Keep form visible - no need to hide/show anything
     292
     293                    // Update the shipping methods display in new results container
     294                    if (t.shipping_methods && t.shipping_methods.trim() !== '') {
     295                        jQuery("#superfrete-results-container").html(t.shipping_methods).show();
     296                        jQuery("#superfrete-status-message").hide(); // Hide status when results are shown
     297                    } else {
     298                        jQuery("#superfrete-results-container").html('').hide();
     299                        jQuery("#superfrete-status-message p").text('❌ Nenhum método de envio encontrado para este CEP');
     300                        jQuery("#superfrete-status-message").show();
     301                    }
     302                   
     303                    // Display any errors
     304                    if (t.error && t.error.trim() !== '') {
     305                        jQuery("#superfrete-error, .superfrete-error").html(t.error);
     306                    } else {
     307                        jQuery("#superfrete-error, .superfrete-error").html('');
     308                    }
     309                   
    204310                    if(jQuery('form.variations_form').length != 0){
    205311                        var product_id = jQuery('input[name="product_id"]', jQuery('form.variations_form')).val();
     
    209315                        jQuery(document).trigger('superfrete_shipping_address_updated', [t]);
    210316                    }
    211 
    212317                }
    213318            }).always(function () {
     
    223328
    224329        this.autoSelectCountry = function () {
    225             var auto_select_country_code = true;
    226             if (auto_select_country_code == false) return;
    227 
     330            var auto_select_country_code = 'BR'; // Always Brazil for SuperFrete
    228331            jQuery("#calc_shipping_country option[value='" + auto_select_country_code + "']").prop('selected', 'selected');
    229332            jQuery("#calc_shipping_country").trigger('change');
    230 
    231         }
    232 
     333        }
     334
     335        this.initCEPMasking = function () {
     336            var parent = this;
     337           
     338            // CEP input masking and auto-submit functionality
     339            jQuery(document).on('input', '#calc_shipping_postcode', function(e) {
     340                var input = jQuery(this);
     341                var currentValue = input.val();
     342                var cursorPosition = input[0].selectionStart;
     343                var value = currentValue.replace(/\D/g, ''); // Remove non-digits
     344               
     345                // Limit to 8 digits maximum
     346                if (value.length > 8) {
     347                    value = value.substring(0, 8);
     348                }
     349               
     350                // Apply CEP mask (00000-000)
     351                var formattedValue = value;
     352                if (value.length >= 5) {
     353                    formattedValue = value.substring(0, 5) + '-' + value.substring(5, 8);
     354                }
     355               
     356                // Update input value only if it changed
     357                if (currentValue !== formattedValue) {
     358                    input.val(formattedValue);
     359                   
     360                    // Restore cursor position after formatting
     361                    var newCursorPos = cursorPosition;
     362                    if (cursorPosition > 5 && formattedValue.length > 5) {
     363                        newCursorPos = cursorPosition;
     364                    } else if (cursorPosition === 5 && formattedValue.length > 5) {
     365                        newCursorPos = 6; // Skip the dash
     366                    }
     367                   
     368                    setTimeout(function() {
     369                        input[0].setSelectionRange(newCursorPos, newCursorPos);
     370                    }, 0);
     371                }
     372               
     373               
     374                // Update status message and auto-calculate when CEP is complete
     375                parent.updateCEPStatus(value);
     376            });
     377           
     378            // Handle autocomplete change events
     379            jQuery(document).on('change', '#calc_shipping_postcode', function(e) {
     380                var input = jQuery(this);
     381                var value = input.val().replace(/\D/g, '');
     382               
     383                // If autocomplete filled the field, format and calculate
     384                if (value.length > 0) {
     385                    // Apply formatting
     386                    var formattedValue = value;
     387                    if (value.length >= 5) {
     388                        formattedValue = value.substring(0, 5) + '-' + value.substring(5, 8);
     389                    }
     390                    input.val(formattedValue);
     391                   
     392                    // Update status and calculate if complete
     393                    parent.updateCEPStatus(value);
     394                }
     395            });
     396
     397            // Prevent non-numeric input except dash
     398            jQuery(document).on('keypress', '#calc_shipping_postcode', function(e) {
     399                var char = String.fromCharCode(e.which);
     400                if (!/[0-9-]/.test(char)) {
     401                    e.preventDefault();
     402                }
     403            });
     404
     405            // Handle paste events
     406            jQuery(document).on('paste', '#calc_shipping_postcode', function(e) {
     407                var parent_calc = parent;
     408                setTimeout(function() {
     409                    var input = jQuery('#calc_shipping_postcode');
     410                    var value = input.val().replace(/\D/g, '');
     411                   
     412                    // Limit to 8 digits
     413                    if (value.length > 8) {
     414                        value = value.substring(0, 8);
     415                    }
     416                   
     417                    // Apply CEP mask
     418                    var formattedValue = value;
     419                    if (value.length >= 5) {
     420                        formattedValue = value.substring(0, 5) + '-' + value.substring(5, 8);
     421                    }
     422                   
     423                    input.val(formattedValue);
     424                   
     425                    // Update status and calculate if complete
     426                    parent_calc.updateCEPStatus(value);
     427                }, 0);
     428            });
     429        }
     430       
     431        this.updateCEPStatus = function(cleanValue) {
     432            var parent = this;
     433           
     434            if (cleanValue.length === 0) {
     435                jQuery("#superfrete-status-message p").text('💡 Digite seu CEP para calcular automaticamente o frete e prazo de entrega');
     436                jQuery("#superfrete-results-container").hide();
     437                jQuery("#superfrete-status-message").show();
     438            } else if (cleanValue.length < 8) {
     439                jQuery("#superfrete-status-message p").text('⌨️ Continue digitando seu CEP...');
     440                jQuery("#superfrete-results-container").hide();
     441                jQuery("#superfrete-status-message").show();
     442            } else if (cleanValue.length === 8) {
     443                jQuery("#superfrete-status-message p").text('🔄 Calculando frete automaticamente...');
     444                // Check if this is a new CEP (different from last calculated)
     445                var lastCalculatedCEP = jQuery("#calc_shipping_postcode").data('last-calculated-cep');
     446                if (lastCalculatedCEP !== cleanValue) {
     447                    // Store the CEP being calculated
     448                    jQuery("#calc_shipping_postcode").data('last-calculated-cep', cleanValue);
     449                    // Small delay to ensure user sees the formatted CEP
     450                    setTimeout(function() {
     451                        if (parent.form_present()) {
     452                            parent.onloadShippingMethod();
     453                        }
     454                    }, 300);
     455                }
     456            }
     457        }
    233458    }
    234459
  • superfrete/trunk/assets/styles/superfrete.css

    r3289983 r3341621  
     1/* SuperFrete Theme Compatibility Layer */
     2:root {
     3    /* Brand Colors - Can be overridden by themes */
     4    --superfrete-primary-color: #0fae79;
     5    --superfrete-primary-hover: #0d9969;
     6    --superfrete-secondary-color: #EA961F;
     7    --superfrete-secondary-hover: #d88519;
     8   
     9    /* Theme-inherited colors */
     10    --superfrete-text-color: inherit;
     11    --superfrete-heading-color: inherit;
     12    --superfrete-bg-color: #f8f9fa;
     13    --superfrete-bg-white: #ffffff;
     14    --superfrete-border-color: #ddd;
     15    --superfrete-border-light: #eaeaea;
     16   
     17    /* Status colors */
     18    --superfrete-success-color: #4CAF50;
     19    --superfrete-error-color: #e74c3c;
     20    --superfrete-info-color: #2196F3;
     21   
     22    /* Typography - Inherit from theme */
     23    --superfrete-font-family: inherit;
     24    --superfrete-font-size-base: 1rem;
     25    --superfrete-font-size-small: 0.875rem;
     26    --superfrete-font-size-large: 1.125rem;
     27    --superfrete-font-weight-normal: normal;
     28    --superfrete-font-weight-bold: bold;
     29    --superfrete-line-height: 1.5;
     30   
     31    /* Spacing */
     32    --superfrete-spacing-xs: 0.25rem;
     33    --superfrete-spacing-sm: 0.5rem;
     34    --superfrete-spacing-md: 1rem;
     35    --superfrete-spacing-lg: 1.5rem;
     36    --superfrete-spacing-xl: 2rem;
     37   
     38    /* Border radius */
     39    --superfrete-radius-sm: 4px;
     40    --superfrete-radius-md: 6px;
     41    --superfrete-radius-lg: 8px;
     42   
     43    /* Shadows */
     44    --superfrete-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
     45    --superfrete-shadow-md: 0 2px 5px rgba(0, 0, 0, 0.1);
     46    --superfrete-shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.15);
     47   
     48    /* Z-index */
     49    --superfrete-z-overlay: 999;
     50    --superfrete-z-popup: 1000;
     51    --superfrete-z-loading: 1001;
     52}
     53
     54/* Dark mode support */
     55@media (prefers-color-scheme: dark) {
     56    :root {
     57        --superfrete-bg-color: #2a2a2a;
     58        --superfrete-bg-white: #3a3a3a;
     59        --superfrete-border-color: #555;
     60        --superfrete-border-light: #444;
     61    }
     62}
     63
    164.superfrete-popup {
    265    display: none;
    366    position: fixed;
    4     z-index: 1000;
     67    z-index: var(--superfrete-z-popup);
    568    left: 0;
    669    top: 0;
     
    1174
    1275.superfrete-popup-content {
    13     background: white;
     76    background: var(--superfrete-bg-white);
    1477    width: 50%;
    1578    margin: 10% auto;
    16     padding: 20px;
    17     border-radius: 8px;
     79    padding: var(--superfrete-spacing-lg);
     80    border-radius: var(--superfrete-radius-lg);
    1881    text-align: center;
    1982}
     
    2184.superfrete-popup-close {
    2285    float: right;
    23     font-size: 24px;
     86    font-size: 1.5rem;
    2487    cursor: pointer;
     88    color: var(--superfrete-text-color);
    2589}
    2690#superfrete-calculator {
    27     background: #f8f9fa;
    28     padding: 15px;
    29     border-radius: 8px;
    30     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     91    background: var(--superfrete-bg-color);
     92    padding: var(--superfrete-spacing-md);
     93    border-radius: var(--superfrete-radius-lg);
     94    box-shadow: var(--superfrete-shadow-md);
    3195    max-width: 350px;
    32     margin: 20px auto;
    33     font-family: Arial, sans-serif;
     96    margin: var(--superfrete-spacing-lg) auto;
     97    font-family: var(--superfrete-font-family);
    3498    display: block;
    3599    margin-left: 0;
     
    37101
    38102#superfrete-calculator label {
    39     font-size: 14px;
    40     font-weight: bold;
    41     color: #333;
     103    font-size: var(--superfrete-font-size-small);
     104    font-weight: var(--superfrete-font-weight-bold);
     105    color: var(--superfrete-heading-color);
    42106    display: block;
    43     margin-bottom: 5px;
     107    margin-bottom: var(--superfrete-spacing-xs);
    44108}
    45109
    46110#superfrete-calculator input[type="text"] {
    47111    width: -webkit-fill-available;
    48     padding: 10px;
    49     font-size: 14px;
    50     border: 1px solid #ccc;
    51     border-radius: 5px;
    52     margin-bottom: 10px;
     112    padding: var(--superfrete-spacing-sm);
     113    font-size: var(--superfrete-font-size-base);
     114    border: 1px solid var(--superfrete-border-color);
     115    border-radius: var(--superfrete-radius-sm);
     116    margin-bottom: var(--superfrete-spacing-sm);
    53117    outline: none;
    54118    transition: border 0.3s ease-in-out;
     119    font-family: var(--superfrete-font-family);
     120    color: var(--superfrete-text-color);
     121    background-color: var(--superfrete-bg-white);
    55122}
    56123
    57124#superfrete-calculator input[type="text"]:focus {
    58     border-color: #0fae79;
     125    border-color: var(--superfrete-primary-color);
    59126}
    60127
    61128#calculate-shipping {
    62     font-weight: 700;
    63     background: #0fae79;
    64     color: #fff;
    65     border: none;
    66     padding: 10px;
    67     width: 100%;
    68     border-radius: 5px;
    69     font-size: 16px;
     129    font-weight: var(--superfrete-font-weight-bold);
     130    background: var(--superfrete-primary-color);
     131    color: white;
     132    border: none;
     133    padding: var(--superfrete-spacing-sm);
     134    width: 100%;
     135    border-radius: var(--superfrete-radius-sm);
     136    font-size: var(--superfrete-font-size-base);
    70137    cursor: pointer;
    71138    transition: background 0.3s ease-in-out;
     139    font-family: var(--superfrete-font-family);
    72140}
    73141
    74142#calculate-shipping:hover {
    75     background: #EA961F;
     143    background: var(--superfrete-secondary-color);
    76144}
    77145
     
    81149
    82150#superfrete-shipping-result h4 {
    83     font-size: 16px;
    84     font-weight: bold;
    85     color: #0fae79;
     151    font-size: var(--superfrete-font-size-base);
     152    font-weight: var(--superfrete-font-weight-bold);
     153    color: var(--superfrete-primary-color);
    86154    text-transform: uppercase;
    87155}
     
    93161
    94162#superfrete-shipping-result li {
    95     background: #ffffff;
    96     padding: 10px;
    97     border-radius: 5px;
    98     margin-top: 8px;
    99     border-left: 5px solid #0fae79;
    100     font-size: 14px;
    101     color: #555;
    102     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
     163    background: var(--superfrete-bg-white);
     164    padding: var(--superfrete-spacing-sm);
     165    border-radius: var(--superfrete-radius-sm);
     166    margin-top: var(--superfrete-spacing-sm);
     167    border-left: 5px solid var(--superfrete-primary-color);
     168    font-size: var(--superfrete-font-size-small);
     169    color: var(--superfrete-text-color);
     170    box-shadow: var(--superfrete-shadow-sm);
    103171}
    104172.superfrete-alert ul li {
    105     background: #ffffff;
    106     padding: 10px;
    107     border-radius: 5px;
    108     margin-top: 8px;
    109     border-left: 5px solid #0fae79;
    110     font-size: 14px;
    111     color: #555;
    112     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
     173    background: var(--superfrete-bg-white);
     174    padding: var(--superfrete-spacing-sm);
     175    border-radius: var(--superfrete-radius-sm);
     176    margin-top: var(--superfrete-spacing-sm);
     177    border-left: 5px solid var(--superfrete-primary-color);
     178    font-size: var(--superfrete-font-size-small);
     179    color: var(--superfrete-text-color);
     180    box-shadow: var(--superfrete-shadow-sm);
    113181}
    114182
     
    118186}
    119187#superfrete-shipping-result li strong {
    120     color: #000;
     188    color: var(--superfrete-heading-color);
    121189}
    122190
     
    141209
    142210.superfrete-popup-content {
    143     background: #f8f9fa;
    144     padding: 20px;
    145     border-radius: 8px;
    146     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     211    background: var(--superfrete-bg-color);
     212    padding: var(--superfrete-spacing-lg);
     213    border-radius: var(--superfrete-radius-lg);
     214    box-shadow: var(--superfrete-shadow-md);
    147215    max-width: 400px;
    148216    width: 90%;
    149217    text-align: center;
    150     font-family: Arial, sans-serif;
     218    font-family: var(--superfrete-font-family);
    151219    position: relative;
    152220}
     
    154222.superfrete-popup-close {
    155223    position: absolute;
    156     top: 10px;
    157     right: 15px;
    158     font-size: 20px;
    159     font-weight: bold;
    160     color: #555;
     224    top: var(--superfrete-spacing-sm);
     225    right: var(--superfrete-spacing-md);
     226    font-size: 1.25rem;
     227    font-weight: var(--superfrete-font-weight-bold);
     228    color: var(--superfrete-text-color);
    161229    cursor: pointer;
    162230    transition: color 0.3s ease-in-out;
     
    164232
    165233.superfrete-popup-close:hover {
    166     color: #EA961F;
     234    color: var(--superfrete-secondary-color);
    167235}
    168236
    169237.superfrete-popup h3 {
    170     font-size: 18px;
    171     font-weight: bold;
    172     color: #0fae79;
    173     margin-bottom: 10px;
     238    font-size: var(--superfrete-font-size-large);
     239    font-weight: var(--superfrete-font-weight-bold);
     240    color: var(--superfrete-primary-color);
     241    margin-bottom: var(--superfrete-spacing-sm);
    174242}
    175243
    176244.superfrete-popup p {
    177     font-size: 14px;
    178     color: #555;
    179     margin-bottom: 15px;
     245    font-size: var(--superfrete-font-size-small);
     246    color: var(--superfrete-text-color);
     247    margin-bottom: var(--superfrete-spacing-md);
    180248}
    181249
     
    185253
    186254.superfrete-popup label {
    187     font-size: 14px;
    188     font-weight: bold;
    189     color: #333;
     255    font-size: var(--superfrete-font-size-small);
     256    font-weight: var(--superfrete-font-weight-bold);
     257    color: var(--superfrete-heading-color);
    190258    display: block;
    191     margin-bottom: 5px;
     259    margin-bottom: var(--superfrete-spacing-xs);
    192260}
    193261
    194262.superfrete-popup input[type="text"] {
    195     width: calc(100% - 20px);
    196     padding: 10px;
    197     font-size: 14px;
    198     border: 1px solid #ccc;
    199     border-radius: 5px;
    200     margin-bottom: 10px;
     263    width: calc(100% - var(--superfrete-spacing-lg));
     264    padding: var(--superfrete-spacing-sm);
     265    font-size: var(--superfrete-font-size-base);
     266    border: 1px solid var(--superfrete-border-color);
     267    border-radius: var(--superfrete-radius-sm);
     268    margin-bottom: var(--superfrete-spacing-sm);
    201269    outline: none;
    202270    transition: border 0.3s ease-in-out;
     271    font-family: var(--superfrete-font-family);
     272    color: var(--superfrete-text-color);
     273    background-color: var(--superfrete-bg-white);
    203274}
    204275
    205276.superfrete-popup input[type="text"]:focus {
    206     border-color: #0fae79;
     277    border-color: var(--superfrete-primary-color);
    207278}
    208279
    209280.superfrete-popup button {
    210     font-weight: 700;
    211     background: #0fae79;
    212     color: #fff;
    213     border: none;
    214     padding: 10px;
    215     width: 100%;
    216     border-radius: 5px;
    217     font-size: 16px;
     281    font-weight: var(--superfrete-font-weight-bold);
     282    background: var(--superfrete-primary-color);
     283    color: white;
     284    border: none;
     285    padding: var(--superfrete-spacing-sm);
     286    width: 100%;
     287    border-radius: var(--superfrete-radius-sm);
     288    font-size: var(--superfrete-font-size-base);
    218289    cursor: pointer;
    219290    transition: background 0.3s ease-in-out;
     291    font-family: var(--superfrete-font-family);
    220292}
    221293
    222294.superfrete-popup button:hover {
    223     background: #EA961F;
     295    background: var(--superfrete-secondary-color);
    224296}
    225297#super-frete-shipping-calculator{
     
    227299}
    228300.superfrete-processing #super-frete-shipping-calculator:after{
    229     content:url(../img/loading-3.gif);
    230     position:absolute;
    231     width:100%;
    232     height:100%;
    233     background-size: auto;
    234     z-index:100;
    235     top:0px;
    236     background-color: #ffffff8c;
     301    content: "";
     302    position: absolute;
     303    width: 100%;
     304    height: 100%;
     305    background-color: rgba(255, 255, 255, 0.85);
     306    z-index: var(--superfrete-z-overlay);
     307    top: 0px;
    237308    display: flex;
    238309    align-content: center;
    239310    justify-content: center;
    240311    align-items: center;
    241     border: 1px solid #021e0e2e;
    242     border-radius: 5px;
     312    border-radius: var(--superfrete-radius-lg);
     313}
     314
     315.superfrete-processing #super-frete-shipping-calculator:before{
     316    content: "";
     317    position: absolute;
     318    top: 50%;
     319    left: 50%;
     320    width: 24px;
     321    height: 24px;
     322    margin-top: -12px;
     323    margin-left: -12px;
     324    border: 2px solid var(--superfrete-primary-color);
     325    border-top: 2px solid transparent;
     326    border-radius: 50%;
     327    z-index: var(--superfrete-z-loading);
     328    animation: superfrete-spin 0.8s linear infinite;
     329}
     330
     331@keyframes superfrete-spin {
     332    0% { transform: rotate(0deg); }
     333    100% { transform: rotate(360deg); }
    243334}
    244335a.button.superfrete-shipping-calculator-button {
    245336    width: 100%;
    246     padding: 10px 0px;
     337    padding: var(--superfrete-spacing-sm) 0;
    247338}
    248339
     
    252343/* Estilização da mensagem de alerta */
    253344.superfrete-alert {
    254     background: #01714c3d;
    255     color: #333;
    256     padding: 10px;
     345    background: rgba(1, 113, 76, 0.24);
     346    color: var(--superfrete-text-color);
     347    padding: var(--superfrete-spacing-sm);
    257348    text-align: center;
    258     font-weight: bold;
    259     border-radius: 5px;
    260     margin-bottom: 10px;
    261     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     349    font-weight: var(--superfrete-font-weight-bold);
     350    border-radius: var(--superfrete-radius-sm);
     351    margin-bottom: var(--superfrete-spacing-sm);
     352    box-shadow: var(--superfrete-shadow-md);
    262353}
    263354
     
    265356.superfrete-container {
    266357        position: relative;
    267     background: #f8f9fa;
    268     padding: 20px;
    269     border-radius: 8px;
    270     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     358    background: var(--superfrete-bg-color);
     359    padding: var(--superfrete-spacing-lg);
     360    border-radius: var(--superfrete-radius-lg);
     361    box-shadow: var(--superfrete-shadow-md);
    271362    max-width: 400px;
    272     margin: 20px auto;
    273     font-family: Arial, sans-serif;
     363    margin: var(--superfrete-spacing-lg) auto;
     364    font-family: var(--superfrete-font-family);
    274365}
    275366
     
    277368.superfrete-shipping-calculator-button {
    278369    display: block;
    279     background: #0fae79;
    280     color: #fff;
     370    background: var(--superfrete-primary-color);
     371    color: white;
    281372    text-align: center;
    282     font-size: 16px;
    283     padding: 12px;
    284     border-radius: 5px;
     373    font-size: var(--superfrete-font-size-base);
     374    padding: var(--superfrete-spacing-md);
     375    border-radius: var(--superfrete-radius-sm);
    285376    text-decoration: none;
    286377    transition: background 0.3s ease-in-out;
    287     font-weight: bold;
     378    font-weight: var(--superfrete-font-weight-bold);
    288379}
    289380
    290381.superfrete-shipping-calculator-button:hover {
    291     background: #EA961F;
     382    background: var(--superfrete-secondary-color);
    292383}
    293384
     
    295386.superfrete-shipping-calculator-form {
    296387    display: none;
    297     margin-top: 15px;
    298     padding: 15px;
    299     background: white;
    300     border-radius: 5px;
    301     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     388    margin-top: var(--superfrete-spacing-md);
     389    padding: var(--superfrete-spacing-md);
     390    background: var(--superfrete-bg-white);
     391    border-radius: var(--superfrete-radius-sm);
     392    box-shadow: var(--superfrete-shadow-md);
    302393}
    303394
    304395/* Mensagem de erro */
    305396.superfrete-error {
    306     color: #d9534f;
    307     padding: 10px;
    308     border-radius: 5px;
    309     margin-bottom: 10px;
    310     font-size: 14px;
     397    color: var(--superfrete-error-color);
     398    padding: var(--superfrete-spacing-sm);
     399    border-radius: var(--superfrete-radius-sm);
     400    margin-bottom: var(--superfrete-spacing-sm);
     401    font-size: var(--superfrete-font-size-small);
    311402    text-align: center;
    312     font-weight: bold;
     403    font-weight: var(--superfrete-font-weight-bold);
    313404}
    314405
     
    317408.superfrete-woocommerce-shipping-calculator input {
    318409    width: 100%;
    319     padding: 12px;
    320     font-size: 14px;
    321     border: 1px solid #ccc;
    322     border-radius: 5px;
    323     margin-bottom: 10px;
     410    padding: var(--superfrete-spacing-md);
     411    font-size: var(--superfrete-font-size-base);
     412    border: 1px solid var(--superfrete-border-color);
     413    border-radius: var(--superfrete-radius-sm);
     414    margin-bottom: var(--superfrete-spacing-sm);
    324415    outline: none;
    325416    transition: border 0.3s ease-in-out;
     417    font-family: var(--superfrete-font-family);
     418    color: var(--superfrete-text-color);
     419    background-color: var(--superfrete-bg-white);
    326420}
    327421
    328422.superfrete-woocommerce-shipping-calculator input:focus,
    329423.superfrete-woocommerce-shipping-calculator select:focus {
    330     border-color: #0fae79;
     424    border-color: var(--superfrete-primary-color);
    331425}
    332426
    333427/* Botão de calcular */
    334428.superfrete-update-address-button {
    335     font-weight: 700;
    336     background: #0fae79;
    337     color: #fff;
    338     border: none;
    339     padding: 12px;
    340     width: 100%;
    341     border-radius: 5px;
    342     font-size: 16px;
     429    font-weight: var(--superfrete-font-weight-bold);
     430    background: var(--superfrete-primary-color);
     431    color: white;
     432    border: none;
     433    padding: var(--superfrete-spacing-md);
     434    width: 100%;
     435    border-radius: var(--superfrete-radius-sm);
     436    font-size: var(--superfrete-font-size-base);
    343437    cursor: pointer;
    344438    transition: background 0.3s ease-in-out;
    345     margin-top: 10px;
     439    margin-top: var(--superfrete-spacing-sm);
     440    font-family: var(--superfrete-font-family);
    346441}
    347442
    348443.superfrete-update-address-button:hover {
    349     background: #EA961F;
     444    background: var(--superfrete-secondary-color);
    350445}
    351446
     
    363458}
    364459
     460/* Admin Color Picker Fix */
     461.woocommerce_page_wc-settings input[type="color"] {
     462    position: relative;
     463    z-index: 1;
     464    outline: none;
     465}
     466
     467.woocommerce_page_wc-settings input[type="color"]:focus {
     468    outline: none;
     469    box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
     470}
     471
     472/* Prevent color picker from affecting page scroll */
     473.woocommerce_page_wc-settings input[type="color"]::-webkit-color-swatch-wrapper {
     474    padding: 0;
     475    border: none;
     476    border-radius: 3px;
     477}
     478
     479.woocommerce_page_wc-settings input[type="color"]::-webkit-color-swatch {
     480    border: none;
     481    border-radius: 3px;
     482}
     483
     484/* Force HTML5 color picker instead of WordPress Iris */
     485.woocommerce_page_wc-settings input[type="color"] {
     486    -webkit-appearance: none;
     487    -moz-appearance: none;
     488    appearance: none;
     489    width: 50px;
     490    height: 40px;
     491    border: 2px solid #ddd;
     492    border-radius: 4px;
     493    background: none;
     494    cursor: pointer;
     495}
     496
     497.woocommerce_page_wc-settings input[type="color"]::-webkit-color-swatch-wrapper {
     498    padding: 0;
     499    border: none;
     500    border-radius: 2px;
     501}
     502
     503.woocommerce_page_wc-settings input[type="color"]::-webkit-color-swatch {
     504    border: none;
     505    border-radius: 2px;
     506}
     507
     508/* Document Field Styles */
     509#billing_document.error {
     510    border-color: var(--superfrete-error-color, #e74c3c) !important;
     511    box-shadow: 0 0 0 1px var(--superfrete-error-color, #e74c3c) !important;
     512}
     513
     514#billing_document:focus {
     515    border-color: var(--superfrete-primary-color, #0fae79) !important;
     516    box-shadow: 0 0 0 1px var(--superfrete-primary-color, #0fae79) !important;
     517}
     518
     519.superfrete-field-feedback {
     520    font-size: var(--superfrete-font-size-small, 12px);
     521    margin-top: var(--superfrete-spacing-xs, 4px);
     522    line-height: 1.4;
     523    animation: fadeIn 0.3s ease-in-out;
     524}
     525
     526@keyframes fadeIn {
     527    from {
     528        opacity: 0;
     529        transform: translateY(-5px);
     530    }
     531    to {
     532        opacity: 1;
     533        transform: translateY(0);
     534    }
     535}
     536
     537/* Document field icon indicator */
     538#billing_document_field::before {
     539    content: "🆔";
     540    position: absolute;
     541    left: 10px;
     542    top: 50%;
     543    transform: translateY(-50%);
     544    font-size: 16px;
     545    opacity: 0.7;
     546    z-index: 1;
     547}
     548
     549#billing_document_field {
     550    position: relative;
     551}
     552
     553#billing_document_field input {
     554    padding-left: 35px !important;
     555}
     556
     557/* Number and Neighborhood field styles */
     558.woocommerce-checkout .form-row .description {
     559    font-size: 12px;
     560    color: #666;
     561    margin-top: 5px;
     562    font-style: italic;
     563    display: block;
     564}
     565
     566/* SuperFrete notice about N/A */
     567.woocommerce-info.superfrete-number-notice {
     568    background-color: #f0f8ff;
     569    border-left: 4px solid var(--superfrete-primary-color, #0fae79);
     570    margin-bottom: 20px;
     571}
     572
     573.woocommerce-info.superfrete-number-notice strong {
     574    color: var(--superfrete-primary-color, #0fae79);
     575}
     576
     577/* Make number field stand out */
     578#billing_number_field label:after,
     579#shipping_number_field label:after {
     580    content: " (ou N/A)";
     581    font-size: 11px;
     582    color: #999;
     583    font-weight: normal;
     584}
  • superfrete/trunk/readme.txt

    r3289983 r3341621  
    55Tested up to: 6.7
    66Requires PHP: 7.4
    7 Stable tag: 2.1.6
     7Stable tag: 3.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    7171
    7272== Changelog ==
     73= 3.1.0 =
     74* 🎨 **Personalização Visual Avançada:** Nova interface completa para personalizar cores e aparência da calculadora de frete
     75* 🔧 **Presets de Tema:** Adicionados presets para tema claro, escuro e auto-detecção baseada no site
     76* 👁️ **Pré-visualização em Tempo Real:** Visualize as alterações instantaneamente enquanto personaliza as cores
     77* 🎯 **UX Melhorada:** Calculadora de frete simplificada com input sempre visível e cálculo automático ao digitar CEP
     78* ⚡ **Performance:** Removidas notificações desnecessárias e otimizado fluxo de cálculo
     79* 🎛️ **Controles Intuitivos:** Interface reorganizada com cores, tipografia e espaçamentos personalizáveis
     80* 🌈 **Compatibilidade de Tema:** Suporte aprimorado para temas claros e escuros com cores adaptáveis
     81* 🔄 **Reset Inteligente:** Botão para restaurar rapidamente as configurações padrão do SuperFrete
     82
    7383= 2.0 =
    7484* Melhorias na interface administrativa para configuração do frete.
  • superfrete/trunk/superfrete.php

    r3289983 r3341621  
    33  Plugin Name: SuperFrete
    44  Description: Plugin that provides integration with the SuperFrete platform.
    5   Version:     2.1.6
     5  Version:     3.3.0
    66  Author:      Super Frete
    7   Author URI:  https://zafarie.com.br/
     7  Author URI:  https://superfrete.com/
    88  Text Domain: superfrete
    99  License:     GPLv2 or later
    1010  License URI: https://www.gnu.org/licenses/gpl-2.0.html
     11  Requires at least: 5.0
     12  Tested up to: 6.8.1
     13  Requires PHP: 7.4
     14  WC requires at least: 3.0
     15  WC tested up to: 9.4
    1116 */
    1217if (!defined('ABSPATH')) {
     
    1419}
    1520
     21
    1622// Inclui a classe principal do plugin
    1723include_once __DIR__ . '/app/App.php';
    1824
    1925// Inicializa o plugin
    20 new SuperFrete\App();
     26new SuperFrete_API\App();
    2127
    2228
    23 add_filter('plugin_action_links_' . plugin_basename(__FILE__), ['SuperFrete\App', 'superfrete_add_settings_link']);
     29add_filter('plugin_action_links_' . plugin_basename(__FILE__), ['SuperFrete_API\App', 'superfrete_add_settings_link']);
     30
     31// Declare compatibility with WooCommerce High-Performance Order Storage (HPOS)
     32// Only declare HPOS compatibility if WooCommerce version supports it (8.2+)
     33add_action('before_woocommerce_init', function() {
     34    if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) {
     35        // Check if WooCommerce version supports HPOS (8.2+)
     36        if (defined('WC_VERSION') && version_compare(WC_VERSION, '8.2', '>=')) {
     37            \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
     38        }
     39    }
     40});
  • superfrete/trunk/templates/woocommerce/shipping-calculator.php

    r3289983 r3341621  
    1616 */
    1717defined('ABSPATH') || exit;
    18 $closed = get_option('superfrete_default_form_display', 'closed');
    19 if ($closed == 'open') {
    20     $style = '';
    21 } else {
    22     $style = ' display:none; ';
     18?>
     19<?php
     20// Allow themes to add custom classes
     21$calculator_classes = apply_filters('superfrete_calculator_classes', array('superfrete-calculator-wrapper'));
     22$calculator_attributes = apply_filters('superfrete_calculator_attributes', array());
     23
     24// Build attributes string
     25$attributes_string = '';
     26foreach ($calculator_attributes as $key => $value) {
     27    $attributes_string .= sprintf(' %s="%s"', esc_attr($key), esc_attr($value));
    2328}
    2429?>
    25 <div id="super-frete-shipping-calculator" >
    26     <div class="superfrete-container">
    27         <?php do_action('superfrete_before_calculate_button'); ?>
    28         <form class="superfrete-woocommerce-shipping-calculator" action="<?php echo esc_url( admin_url('admin-ajax.php') ); ?>" method="post" onsubmit="return false;">
     30<div id="super-frete-shipping-calculator" class="<?php echo esc_attr(implode(' ', $calculator_classes)); ?>"<?php echo $attributes_string; ?>>
     31    <?php do_action('superfrete_before_calculate_form'); ?>
     32   
     33    <!-- CEP Input Section - Always Visible -->
     34    <div class="superfrete-input-section">
     35        <form class="superfrete-woocommerce-shipping-calculator" action="<?php echo esc_url(admin_url('admin-ajax.php')); ?>" method="post" onsubmit="return false;">
     36            <div id="superfrete-error" class="superfrete-error"></div>
     37           
     38            <?php // Hidden fields for Brazil ?>
     39            <input type="hidden" name="calc_shipping_country" id="calc_shipping_country" value="BR">
     40            <input type="hidden" name="calc_shipping_state" id="calc_shipping_state" value="">
     41            <input type="hidden" name="calc_shipping_city" id="calc_shipping_city" value="">
     42           
     43            <?php // CEP input field - always visible ?>
     44            <div class="form-row form-row-wide" id="calc_shipping_postcode_field">
     45                <input type="text" class="input-text" value="<?php echo esc_attr(WC()->customer->get_shipping_postcode()); ?>"
     46                       placeholder="<?php esc_attr_e('Digite seu CEP (00000-000)', 'superfrete'); ?>"
     47                       name="calc_shipping_postcode" id="calc_shipping_postcode" />
     48            </div>
    2949
    30             <?php printf('<a href="/" class="button superfrete-shipping-calculator-button" rel="nofollow">%s</a>', esc_html($button_text)); ?>
    31 
    32             <?php do_action('superfrete_after_calculate_button'); ?>
    33 
    34             <section class="superfrete-shipping-calculator-form" style="<?php echo esc_attr( $style ); ?>">
    35                 <?php do_action('superfrete_before_calculate_form'); ?>
    36                 <div id="superfrete-error" class="superfrete-error"></div>
    37                 <?php if (apply_filters('woocommerce_shipping_calculator_enable_country', true)) : ?>
    38                     <?php
    39                     $countries = WC()->countries->get_shipping_countries();
    40                     $remove_country_field = get_option('superfrete_remove_country', 0);
    41                     ?>
    42                     <?php
    43                     if (count($countries) == 1 && !empty($remove_country_field)) {
    44                         $first_country_key = array_key_first($countries);
    45                         ?> 
    46                         <input type="hidden" name="calc_shipping_country" id="calc_shipping_country" class="country_to_state country_select" rel="calc_shipping_state" value="<?php echo esc_attr($first_country_key); ?>">
    47                     <?php } else { ?>
    48                         <p class="form-row form-row-wide" id="calc_shipping_country_field">
    49                             <select name="calc_shipping_country" id="calc_shipping_country" class="country_to_state country_select" rel="calc_shipping_state">
    50                                 <option value="default"><?php esc_html_e('Select a country / region&hellip;', 'woocommerce'); ?></option>
    51                                 <?php
    52                                 foreach ($countries as $key => $value) {
    53                                     echo '<option value="' . esc_attr($key) . '"' . selected(WC()->customer->get_shipping_country(), esc_attr($key), false) . '>' . esc_html($value) . '</option>';
    54                                 }
    55                                 ?>
    56                             </select>
    57                         </p>
    58                     <?php } ?>
    59 
    60                 <?php endif; ?>
    61 
    62                 <?php
    63                 $remove_state = get_option('superfrete_remove_state', 0);
    64                 if (apply_filters('woocommerce_shipping_calculator_enable_state', true) && empty($remove_state)) :
    65                     ?>
    66                     <p class="form-row form-row-wide" id="calc_shipping_state_field">
    67                         <?php
    68                         $current_cc = "BR";
    69                         $current_r = WC()->customer->get_shipping_state();
    70                         $states = WC()->countries->get_states($current_cc);
    71 
    72                         if (is_array($states) && empty($states)) {
    73                             ?>
    74                             <input type="hidden" name="calc_shipping_state" id="calc_shipping_state" placeholder="<?php esc_attr_e('State / County', 'woocommerce'); ?>" />
    75                             <?php
    76                         } elseif (is_array($states)) {
    77                             ?>
    78                             <span>
    79                                 <select name="calc_shipping_state" class="state_select" id="calc_shipping_state" data-placeholder="<?php esc_attr_e('State / County', 'woocommerce'); ?>">
    80                                     <option value=""><?php esc_html_e('Select an option&hellip;', 'woocommerce'); ?></option>
    81                                     <?php
    82                                     foreach ($states as $ckey => $cvalue) {
    83                                         echo '<option value="' . esc_attr($ckey) . '" ' . selected($current_r, $ckey, false) . '>' . esc_html($cvalue) . '</option>';
    84                                     }
    85                                     ?>
    86                                 </select>
    87                             </span>
    88                             <?php
    89                         } else {
    90                             ?>
    91                             <input type="text" class="input-text" value="<?php echo esc_attr($current_r); ?>" placeholder="<?php esc_attr_e('State / County', 'woocommerce'); ?>" name="calc_shipping_state" id="calc_shipping_state" />
    92                             <?php
    93                         }
    94                         ?>
    95                     </p>
    96                 <?php endif; ?>
    97 
    98                 <?php
    99                 $remove_city = get_option('superfrete_remove_city', 0);
    100                 if (apply_filters('woocommerce_shipping_calculator_enable_city', true) && empty($remove_city)) :
    101                     ?>
    102                     <p class="form-row form-row-wide" id="calc_shipping_city_field">
    103                         <input type="text" class="input-text" value="<?php echo esc_attr(WC()->customer->get_shipping_city()); ?>" placeholder="<?php esc_attr_e('City', 'woocommerce'); ?>" name="calc_shipping_city" id="calc_shipping_city" />
    104                     </p>
    105                 <?php endif; ?>
    106 
    107                 <?php
    108                 $remove_postcode = get_option('superfrete_remove_postcode', 0);
    109                 if (apply_filters('woocommerce_shipping_calculator_enable_postcode', true) && empty($remove_postcode)) :
    110                     ?>
    111                     <p class="form-row form-row-wide" id="calc_shipping_postcode_field">
    112                         <input type="text" class="input-text" value="<?php echo esc_attr(WC()->customer->get_shipping_postcode()); ?>" placeholder="<?php esc_attr_e('Postcode / ZIP', 'woocommerce'); ?>" name="calc_shipping_postcode" id="calc_shipping_postcode" />
    113                     </p>
    114                 <?php endif; ?>
    115 
    116                 <p>
    117                     <button type="submit" name="calc_shipping" value="1" class="button superfrete-update-address-button">
    118                         <?php echo esc_html($update_address_btn_text); ?>
    119                     </button>
    120                 </p>
    121                 <?php wp_nonce_field('superfrete_nonce', 'superfrete_nonce'); ?>
    122                 <?php do_action('superfrete_after_calculate_form'); ?>
    123             </section>
     50            <div class="form-row" id="superfrete-submit-container" style="display: none;">
     51                <button type="submit" name="calc_shipping" value="1" class="button superfrete-update-address-button">
     52                    <?php echo esc_html($update_address_btn_text); ?>
     53                </button>
     54            </div>
     55           
     56            <?php wp_nonce_field('superfrete_nonce', 'superfrete_nonce'); ?>
     57            <?php do_action('superfrete_after_calculate_form'); ?>
     58           
    12459            <?php if (!empty($product_id)): ?>
    12560                <input type="hidden" name="product_id" value="<?php echo esc_attr($product_id); ?>">
     
    13469    </div>
    13570
    136     <div id="superfrete-alert-container" class="superfrete-alert-container"></div>
     71    <!-- Status Message Section -->
     72    <div id="superfrete-status-message" class="superfrete-status-message">
     73        <p><?php esc_html_e('💡 Digite seu CEP para calcular automaticamente o frete e prazo de entrega', 'superfrete'); ?></p>
     74    </div>
     75   
     76    <!-- Results Section -->
     77    <div id="superfrete-results-container" class="superfrete-results-container" style="display:none;"></div>
    13778</div>
Note: See TracChangeset for help on using the changeset viewer.