Changeset 3341621
- Timestamp:
- 08/08/2025 12:13:02 PM (8 months ago)
- Location:
- superfrete/trunk
- Files:
-
- 21 added
- 18 edited
-
. (modified) (1 prop)
-
README-DEV.md (added)
-
THEME_CUSTOMIZATION.md (added)
-
VERSION_REQUIREMENTS.md (added)
-
WEBHOOK_README.md (added)
-
api/Http/Request.php (modified) (2 diffs)
-
api/Http/WebhookVerifier.php (added)
-
app/App.php (modified) (12 diffs)
-
app/Controllers/Admin/Admin_Menu.php (modified) (2 diffs)
-
app/Controllers/Admin/SuperFrete_OrderActions.php (modified) (12 diffs)
-
app/Controllers/Admin/SuperFrete_Settings.php (modified) (7 diffs)
-
app/Controllers/Admin/WebhookAdmin.php (added)
-
app/Controllers/CheckoutFields.php (added)
-
app/Controllers/DocumentFields.php (added)
-
app/Controllers/OAuthController.php (added)
-
app/Controllers/ProductShipping.php (modified) (9 diffs)
-
app/Controllers/SuperFrete_Order.php (modified) (7 diffs)
-
app/Controllers/WebhookController.php (added)
-
app/Controllers/WebhookRetryManager.php (added)
-
app/Helpers/AddressHelper.php (added)
-
app/Helpers/ShippingMigration.php (added)
-
app/Helpers/SuperFrete_Notice.php (modified) (2 diffs)
-
app/Shipping/SuperFreteBase.php (added)
-
app/Shipping/SuperFreteJadlog.php (added)
-
app/Shipping/SuperFreteLoggi.php (added)
-
app/Shipping/SuperFreteMiniEnvio.php (modified) (1 diff)
-
app/Shipping/SuperFretePAC.php (modified) (1 diff)
-
app/Shipping/SuperFreteSEDEX.php (modified) (1 diff)
-
app/Shipping/SuperFreteShipping.php (modified) (3 diffs)
-
assets/js (added)
-
assets/js/document-field.js (added)
-
assets/scripts/superfrete-calculator.js (modified) (11 diffs)
-
assets/styles/superfrete-calculator.css (added)
-
assets/styles/superfrete.css (modified) (18 diffs)
-
database (added)
-
database/webhook_migrations.php (added)
-
readme.txt (modified) (2 diffs)
-
superfrete.php (modified) (2 diffs)
-
templates/woocommerce/shipping-calculator.php (modified) (2 diffs)
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
-
Property
svn:ignore
set to
-
superfrete/trunk/api/Http/Request.php
r3289983 r3341621 14 14 */ 15 15 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')); 24 31 } 25 32 … … 29 36 public function call_superfrete_api($endpoint, $method = 'GET', $payload = [], $retorno = false) { 30 37 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 31 51 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 37 206 ]; 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 44 237 ]; 45 238 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 } 60 293 } 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 1 1 <?php 2 2 3 namespace SuperFrete ;3 namespace SuperFrete_API; 4 4 5 5 use SuperFrete_API\Helpers\Logger; … … 22 22 add_action('woocommerce_shipping_init', function () { 23 23 if (class_exists('WC_Shipping_Method')) { 24 require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteBase.php'; 24 25 require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFretePAC.php'; 25 26 require_once plugin_dir_path(__FILE__) . 'Shipping/SuperFreteSEDEX.php'; 26 27 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'; 27 30 } 28 31 }); 29 32 30 33 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 36 44 return $methods; 37 45 }); … … 47 55 require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/SuperFrete_Settings.php'; 48 56 require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/Admin_Menu.php'; 57 require_once plugin_dir_path(__FILE__) . 'Controllers/Admin/WebhookAdmin.php'; 49 58 require_once plugin_dir_path(__FILE__) . '../api/Http/Request.php'; 59 require_once plugin_dir_path(__FILE__) . '../api/Http/WebhookVerifier.php'; 50 60 require_once plugin_dir_path(__FILE__) . '../api/Helpers/Logger.php'; 51 61 require_once plugin_dir_path(__FILE__) . 'Controllers/ProductShipping.php'; 52 62 require_once plugin_dir_path(__FILE__) . 'Controllers/SuperFrete_Order.php'; 53 63 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 } 55 78 } 56 79 … … 63 86 new \SuperFrete_API\Admin\SuperFrete_OrderActions(); 64 87 new \SuperFrete_API\Admin\SuperFrete_Settings(); 88 new \SuperFrete_API\Admin\WebhookAdmin(); 65 89 new \SuperFrete_API\Controllers\ProductShipping(); 66 90 if (class_exists('\SuperFrete_API\Admin\Admin_Menu')) { … … 68 92 } 69 93 new \SuperFrete_API\Controllers\SuperFrete_Order(); 94 new \SuperFrete_API\Controllers\WebhookController(); 95 new \SuperFrete_API\Controllers\WebhookRetryManager(); 96 new \SuperFrete_API\Controllers\OAuthController(); 70 97 \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 71 104 add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']); 72 105 add_action('wp', function () { … … 75 108 } 76 109 }); 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); 78 114 79 115 // Adiciona os campos 'Número' e 'Bairro' nas configurações da loja … … 95 131 add_action('superfrete_clear_log_event', function () { 96 132 \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 } 99 186 if (!class_exists('\WC_Shipping_Zones')) return; 100 187 … … 123 210 ]); 124 211 } 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 } 144 239 145 240 error_log('✅ Zona de entrega "Brasil - SuperFrete" criada com o método ativado.'); … … 152 247 return $rates; 153 248 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) 155 257 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)); 158 282 159 283 return $rates; … … 198 322 } 199 323 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 } 200 345 201 346 public function superfrete_configs_setup_notice() … … 265 410 266 411 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); 269 429 wp_enqueue_script( 270 430 'superfrete-popup', … … 340 500 wp_send_json_success(['message' => 'Campos vazios preenchidos e endereço atualizado!', 'order_id' => $order_id]); 341 501 } 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 } 342 734 } -
superfrete/trunk/app/Controllers/Admin/Admin_Menu.php
r3289983 r3341621 1 1 <?php 2 3 namespace SuperFrete_API\Admin; 2 4 3 5 if (!defined('ABSPATH')) { … … 5 7 } 6 8 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 ); 9 class Admin_Menu { 10 11 public function __construct() { 12 add_action('admin_menu', [$this, 'superfrete_add_admin_menu']); 13 } 19 14 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 ); 30 27 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§ion=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 } 38 37 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§ion=options#superfrete_settings_section-description')); 43 exit; 44 } 45 45 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>'; 50 52 51 echo '<form method="post" action="">';52 wp_nonce_field('clear_log_action'); // Adicionar verificação de nonce53 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>'; 55 57 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>'; 57 62 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 } 62 70 } 63 71 } -
superfrete/trunk/app/Controllers/Admin/SuperFrete_OrderActions.php
r3289983 r3341621 21 21 // Adiciona o AJAX para verificar o status da etiqueta 22 22 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; 23 49 } 24 50 … … 114 140 115 141 $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); 117 143 118 144 if (!$etiqueta_id) { … … 126 152 $superfrete_tracking = $this->get_superfrete_data($etiqueta_id)['tracking']; 127 153 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)); 129 155 130 156 echo "<p><strong>" . esc_html__('Saldo na SuperFrete:', 'superfrete') . "</strong> R$ " . esc_html(number_format($saldo, 2, ',', '.')) . "</p>"; … … 136 162 137 163 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>';140 164 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>'; 141 167 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>'; 142 170 } 143 171 } else if ($superfrete_status == 'canceled') { … … 148 176 } else if ($superfrete_status == 'posted') { 149 177 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>'; 151 194 152 195 } else if ($superfrete_status == 'delivered') { 153 196 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 } 154 208 155 209 } else { … … 172 226 } 173 227 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 174 246 private function get_superfrete_data($id) 175 247 { … … 184 256 private function get_ticket_superfrete($order_id) 185 257 { 186 $etiqueta_id = get_post_meta($order_id, '_superfrete_id', true);258 $etiqueta_id = $this->get_order_meta($order_id, '_superfrete_id', true); 187 259 if (!$etiqueta_id) { 188 260 return ''; … … 193 265 194 266 if (isset($response['url'])) { 195 update_post_meta($order_id, '_superfrete_status', 'success');267 $this->update_order_meta($order_id, '_superfrete_status', 'success'); 196 268 return $response['url']; 197 269 } … … 222 294 223 295 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'); 225 297 Logger::log('SuperFrete', "Pedido #{$order_id} enviado com sucesso."); 226 298 } else { 227 update_post_meta($order_id, '_superfrete_status', 'erro');299 $this->update_order_meta($order_id, '_superfrete_status', 'erro'); 228 300 Logger::log('SuperFrete', "Erro ao reenviar pedido #{$order_id}."); 229 301 } … … 251 323 Logger::log('SuperFrete', "Pagando Etiqueta #{$order_id}..."); 252 324 253 $etiqueta_id = get_post_meta($order_id, '_superfrete_id', true);325 $etiqueta_id = $this->get_order_meta($order_id, '_superfrete_id', true); 254 326 255 327 $request = new Request(); … … 257 329 258 330 if ($response == 409) { 259 update_post_meta($order_id, '_superfrete_status', 'success');331 $this->update_order_meta($order_id, '_superfrete_status', 'success'); 260 332 wp_redirect(admin_url('post.php?post=' . $order_id . '&action=edit')); 261 333 return; … … 264 336 if (isset($response['status']) && $response['status'] === 'pending') { 265 337 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'); 267 339 Logger::log('SuperFrete', "Pedido #{$order_id} enviado com sucesso."); 268 340 } else { 269 update_post_meta($order_id, '_superfrete_status', 'aguardando');341 $this->update_order_meta($order_id, '_superfrete_status', 'aguardando'); 270 342 271 343 Logger::log('SuperFrete', "Erro ao tentar pagar o ticket do pedido #{$order_id}."); -
superfrete/trunk/app/Controllers/Admin/SuperFrete_Settings.php
r3289983 r3341621 3 3 4 4 use SuperFrete_API\Http\Request; 5 use SuperFrete_API\Helpers\Logger; 5 6 if (!defined('ABSPATH')) { 6 7 exit; // Segurança para evitar acesso direto … … 33 34 } 34 35 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 } 41 95 } 42 96 } … … 47 101 public static function add_superfrete_settings($settings) { 48 102 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 49 109 $request = new Request(); 50 110 $response = $request->call_superfrete_api('/api/v0/user', 'GET', [], true); 51 111 52 112 $is_connected = ($response && isset($response['id'])); 113 $user_name = $is_connected ? $response['firstname'] . " " . $response['lastname'] : ''; 53 114 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(); 56 117 57 118 // Garante que as configurações antigas sejam migradas antes de exibir a página de configurações … … 65 126 ]; 66 127 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 } 83 167 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[] = [ 85 340 'title' => 'Ativar Sandbox', 86 341 'desc' => 'Habilitar ambiente de testes', … … 88 343 'type' => 'checkbox', 89 344 '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' 111 354 ]; 112 355 113 356 $settings[] = [ 114 357 'type' => 'sectionend', 115 'id' => 'superfrete_ settings_section'358 'id' => 'superfrete_advanced_section' 116 359 ]; 117 360 … … 123 366 */ 124 367 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"> 129 380 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'); 133 399 } else { 134 $('.superfrete-sandbox-field').closest('tr').hide(); 400 $advancedFields.slideDown(); 401 $toggle.text('▲ Ocultar Configurações Avançadas'); 135 402 } 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 }); 136 796 } 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(); 144 919 }); 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!'); 145 1046 }); 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(); 149 1586 } 150 1587 } … … 155 1592 // Hook para adicionar a aba dentro de "Entrega" 156 1593 add_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']); 1594 add_action('admin_init', ['SuperFrete_API\Admin\SuperFrete_Settings', 'enqueue_admin_scripts']); 1595 1596 // AJAX hooks for webhook management 1597 add_action('wp_ajax_superfrete_register_webhook', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_webhook_registration']); 1598 add_action('wp_ajax_superfrete_oauth_callback', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_oauth_callback']); 1599 1600 // AJAX hooks for visual customization 1601 add_action('wp_ajax_superfrete_save_customization', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_save_customization']); 1602 add_action('wp_ajax_superfrete_reset_customization', ['SuperFrete_API\Admin\SuperFrete_Settings', 'handle_reset_customization']); -
superfrete/trunk/app/Controllers/ProductShipping.php
r3289983 r3341621 12 12 13 13 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')); 19 16 add_shortcode('pi_shipping_calculator', array($this, 'calculator_shortcode')); 20 17 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') ); 22 19 add_action('woocommerce_after_add_to_cart_form', [$this, 'enqueue_scripts']); 23 20 add_action('wp_ajax_superfrete_calculate', [$this, 'calculate_shipping']); … … 27 24 add_action('wp_ajax_nopriv_superfrete_cal_shipping', array(__CLASS__, 'applyShipping')); 28 25 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); 29 29 } 30 30 … … 57 57 global $product; 58 58 59 // Hide calculator for virtual products (they don't need shipping) 60 if (is_object($product) && $product->is_virtual()) { 61 return; 62 } 63 59 64 if (apply_filters('superfrete_hide_calculator_on_single_product_page', false, $product)) { 60 65 return; … … 89 94 wp_send_json_error(['message' => 'Requisição inválida.'], 403); 90 95 } 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 91 106 if (!class_exists('WC_Shortcode_Cart')) { 92 107 include_once WC_ABSPATH . 'includes/shortcodes/class-wc-shortcode-cart.php'; … … 95 110 96 111 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'])) { 101 115 $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'))); 102 116 wp_send_json($return); 103 117 } 118 119 $log_data['steps']['initial_validation'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; 120 $step_start = microtime(true); 104 121 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 131 122 $return = array(); 132 123 \WC_Shortcode_Cart::calculate_shipping(); 133 124 WC()->cart->calculate_totals(); 134 125 135 136 126 $log_data['steps']['calculate_shipping'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; 127 $step_start = microtime(true); 137 128 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']); 145 301 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 153 378 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 }163 379 } 164 380 wp_die(); … … 362 578 363 579 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 365 581 366 582 if ($auto_loading == 'enabled') … … 371 587 372 588 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>'; 390 624 } 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; 399 646 } 400 647 … … 466 713 */ 467 714 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 476 725 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 483 739 ); 484 740 … … 486 742 'ajax_url' => admin_url('admin-ajax.php'), 487 743 ]); 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 ]); 488 755 } 489 756 } -
superfrete/trunk/app/Controllers/SuperFrete_Order.php
r3289983 r3341621 6 6 use SuperFrete_API\Helpers\Logger; 7 7 use SuperFrete_API\Helpers\SuperFrete_Notice; 8 use SuperFrete_API\Helpers\AddressHelper; 8 9 use WC_Order; 9 10 … … 33 34 return; 34 35 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') : ''; 36 38 if ($superfrete_status == 'enviado') 37 39 return; … … 71 73 // Verifica e adiciona os campos personalizados 72 74 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 75 107 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 78 175 $destinatario = [ 79 176 'name' => $shipping['first_name'] . ' ' . $shipping['last_name'], … … 86 183 'postal_code' => preg_replace('/[^\p{L}\p{N}\s]/', '', $shipping['postcode']) 87 184 ]; 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 } 88 193 89 194 // Obtém o método de envio escolhido … … 92 197 93 198 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 . ')'); 102 242 } 103 243 } … … 186 326 'to' => $destinatario, 187 327 'email' => $order->get_billing_email(), 188 'service' => $service,328 'service' => intval($service), 189 329 'products' => $produtos, 190 330 'volumes' => $volume_data, … … 251 391 252 392 Logger::log('SuperFrete', 'Resposta da API para o pedido #' . $order_id . ': ' . wp_json_encode($response)); 393 253 394 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 259 405 return $response; 260 406 } -
superfrete/trunk/app/Helpers/SuperFrete_Notice.php
r3289983 r3341621 13 13 */ 14 14 public static function add_error($order_id, $message, $missing_fields = []) { 15 if (!session_id() ) {15 if (!session_id() && !headers_sent()) { 16 16 session_start(); 17 17 } … … 28 28 */ 29 29 public static function display_errors() { 30 if (!session_id() ) {30 if (!session_id() && !headers_sent()) { 31 31 session_start(); 32 32 } -
superfrete/trunk/app/Shipping/SuperFreteMiniEnvio.php
r3289983 r3341621 3 3 namespace SuperFrete_API\Shipping; 4 4 5 use SuperFrete_API\Http\Request;6 use SuperFrete_API\Helpers\Logger;7 8 5 if (!defined('ABSPATH')) 9 6 exit; // Segurança 10 7 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'); 8 require_once plugin_dir_path(__FILE__) . 'SuperFreteBase.php'; 76 9 77 add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) ); 10 class 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); 78 18 } 19 79 20 protected function get_service_id() { 80 21 return 17; // ID do Mini Envios na API 81 22 } 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 ];116 23 } 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"=>false133 ],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_custo166 ];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 salvar175 }176 } -
superfrete/trunk/app/Shipping/SuperFretePAC.php
r3289983 r3341621 3 3 namespace SuperFrete_API\Shipping; 4 4 5 use SuperFrete_API\Http\Request;6 use SuperFrete_API\Helpers\Logger;7 8 5 if (!defined('ABSPATH')) 9 6 exit; // Segurança 10 7 11 class SuperFretePAC extends \WC_Shipping_Method { 8 require_once plugin_dir_path(__FILE__) . 'SuperFreteBase.php'; 12 9 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'); 10 class SuperFretePAC extends SuperFreteBase { 77 11 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); 79 18 } 19 80 20 protected function get_service_id() { 81 21 return 1; // ID do PAC na API 82 22 } 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 ];116 23 } 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"=>false133 ],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_custo166 ];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 salvar175 }176 } -
superfrete/trunk/app/Shipping/SuperFreteSEDEX.php
r3289983 r3341621 3 3 namespace SuperFrete_API\Shipping; 4 4 5 use SuperFrete_API\Http\Request;6 use SuperFrete_API\Helpers\Logger;7 8 5 if (!defined('ABSPATH')) 9 6 exit; // Segurança 10 7 11 class SuperFreteSEDEX extends \WC_Shipping_Method { 8 require_once plugin_dir_path(__FILE__) . 'SuperFreteBase.php'; 12 9 13 public $free_shipping; 14 public $extra_days; 15 public $extra_cost; 16 public $extra_cost_type; 10 class SuperFreteSEDEX extends SuperFreteBase { 17 11 18 public function __construct($instance_id = 'superfrete_sedex') {12 public function __construct($instance_id = 0) { 19 13 $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); 79 18 } 80 19 81 20 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 151 22 } 152 23 } 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_custo160 ];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 salvar169 }170 } -
superfrete/trunk/app/Shipping/SuperFreteShipping.php
r3289983 r3341621 5 5 use SuperFrete_API\Http\Request; 6 6 use SuperFrete_API\Helpers\Logger; 7 use SuperFrete_API\Helpers\AddressHelper; 7 8 8 9 if (!defined('ABSPATH')) … … 11 12 class SuperFreteShipping extends \WC_Shipping_Method { 12 13 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 13 18 public function __construct($instance_id = 0) { 14 $this->id = 'superfrete ';19 $this->id = 'superfrete_shipping'; 15 20 $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'), 42 31 'type' => 'checkbox', 43 32 'label' => __('Ativar SuperFrete nas áreas de entrega', 'superfrete'), 44 33 'default' => 'yes', 45 ],46 'title' => [47 'title' => __('Título ', 'superfrete'),34 ), 35 'title' => array( 36 'title' => __('Título do Método'), 48 37 'type' => 'text', 49 38 '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 58 281 */ 59 282 public function calculate_shipping($package = []) { … … 62 285 } 63 286 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 64 293 $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 = []; 68 347 foreach ($package['contents'] as $item) { 69 348 $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']; 76 376 $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); 81 400 82 401 $payload = [ 83 402 'from' => ['postal_code' => $cep_origem], 84 403 '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, 87 412 ]; 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 88 417 $request = new Request(); 89 418 $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']; 100 536 } 537 $frete_desc = ""; 101 538 } 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 ]; 103 720 } 104 721 } -
superfrete/trunk/assets/scripts/superfrete-calculator.js
r3289983 r3341621 1 1 (function ($) { 2 2 '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 } 3 14 4 15 function managingCalculator() { … … 26 37 this.variationChange(); 27 38 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 29 47 } 30 48 31 49 this.cal_init = function(){ 32 this.calculatorOpen();33 50 this.submitDetect(); 34 if(!window?.superfrete_autoloading_done){35 this.onloadShippingMethod(true);36 }37 51 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 } 38 68 } 39 69 … … 86 116 }else{ 87 117 parent.showCalculator(); 88 window.superfrete_autoloading_done = 1;89 118 parent.setVariation(data); 90 119 parent.noVariationSelectedMessage(false); … … 114 143 115 144 this.hideCalculator = function () { 116 jQuery(".superfrete-container").fadeOut();145 // Calculator is always visible now, no need to hide 117 146 } 118 147 119 148 this.showCalculator = function () { 120 jQuery(".superfrete-container").fadeIn();149 // Calculator is always visible now, no need to show 121 150 } 122 151 … … 128 157 } 129 158 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 131 161 } 132 162 … … 134 164 var parent = this; 135 165 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 });144 166 } 145 167 … … 164 186 if(this.form_present() == false) return; 165 187 166 var e = jQuery('form.superfrete-woocommerce-shipping-calculator :visible').first();188 var e = jQuery('form.superfrete-woocommerce-shipping-calculator').first(); 167 189 var parent = this; 168 190 if (jQuery("#superfrete-variation-id").length && jQuery("#superfrete-variation-id").val() == 0) { … … 188 210 * with this one ajax request is reduced when auto loading is set to off 189 211 */ 190 191 192 193 212 jQuery.ajax({ 194 213 type: e.attr("method"), … … 197 216 dataType: "json", 198 217 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 204 310 if(jQuery('form.variations_form').length != 0){ 205 311 var product_id = jQuery('input[name="product_id"]', jQuery('form.variations_form')).val(); … … 209 315 jQuery(document).trigger('superfrete_shipping_address_updated', [t]); 210 316 } 211 212 317 } 213 318 }).always(function () { … … 223 328 224 329 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 228 331 jQuery("#calc_shipping_country option[value='" + auto_select_country_code + "']").prop('selected', 'selected'); 229 332 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 } 233 458 } 234 459 -
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 1 64 .superfrete-popup { 2 65 display: none; 3 66 position: fixed; 4 z-index: 1000;67 z-index: var(--superfrete-z-popup); 5 68 left: 0; 6 69 top: 0; … … 11 74 12 75 .superfrete-popup-content { 13 background: white;76 background: var(--superfrete-bg-white); 14 77 width: 50%; 15 78 margin: 10% auto; 16 padding: 20px;17 border-radius: 8px;79 padding: var(--superfrete-spacing-lg); 80 border-radius: var(--superfrete-radius-lg); 18 81 text-align: center; 19 82 } … … 21 84 .superfrete-popup-close { 22 85 float: right; 23 font-size: 24px;86 font-size: 1.5rem; 24 87 cursor: pointer; 88 color: var(--superfrete-text-color); 25 89 } 26 90 #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); 31 95 max-width: 350px; 32 margin: 20pxauto;33 font-family: Arial, sans-serif;96 margin: var(--superfrete-spacing-lg) auto; 97 font-family: var(--superfrete-font-family); 34 98 display: block; 35 99 margin-left: 0; … … 37 101 38 102 #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); 42 106 display: block; 43 margin-bottom: 5px;107 margin-bottom: var(--superfrete-spacing-xs); 44 108 } 45 109 46 110 #superfrete-calculator input[type="text"] { 47 111 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); 53 117 outline: none; 54 118 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); 55 122 } 56 123 57 124 #superfrete-calculator input[type="text"]:focus { 58 border-color: #0fae79;125 border-color: var(--superfrete-primary-color); 59 126 } 60 127 61 128 #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); 70 137 cursor: pointer; 71 138 transition: background 0.3s ease-in-out; 139 font-family: var(--superfrete-font-family); 72 140 } 73 141 74 142 #calculate-shipping:hover { 75 background: #EA961F;143 background: var(--superfrete-secondary-color); 76 144 } 77 145 … … 81 149 82 150 #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); 86 154 text-transform: uppercase; 87 155 } … … 93 161 94 162 #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); 103 171 } 104 172 .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); 113 181 } 114 182 … … 118 186 } 119 187 #superfrete-shipping-result li strong { 120 color: #000;188 color: var(--superfrete-heading-color); 121 189 } 122 190 … … 141 209 142 210 .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); 147 215 max-width: 400px; 148 216 width: 90%; 149 217 text-align: center; 150 font-family: Arial, sans-serif;218 font-family: var(--superfrete-font-family); 151 219 position: relative; 152 220 } … … 154 222 .superfrete-popup-close { 155 223 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); 161 229 cursor: pointer; 162 230 transition: color 0.3s ease-in-out; … … 164 232 165 233 .superfrete-popup-close:hover { 166 color: #EA961F;234 color: var(--superfrete-secondary-color); 167 235 } 168 236 169 237 .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); 174 242 } 175 243 176 244 .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); 180 248 } 181 249 … … 185 253 186 254 .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); 190 258 display: block; 191 margin-bottom: 5px;259 margin-bottom: var(--superfrete-spacing-xs); 192 260 } 193 261 194 262 .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); 201 269 outline: none; 202 270 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); 203 274 } 204 275 205 276 .superfrete-popup input[type="text"]:focus { 206 border-color: #0fae79;277 border-color: var(--superfrete-primary-color); 207 278 } 208 279 209 280 .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); 218 289 cursor: pointer; 219 290 transition: background 0.3s ease-in-out; 291 font-family: var(--superfrete-font-family); 220 292 } 221 293 222 294 .superfrete-popup button:hover { 223 background: #EA961F;295 background: var(--superfrete-secondary-color); 224 296 } 225 297 #super-frete-shipping-calculator{ … … 227 299 } 228 300 .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; 237 308 display: flex; 238 309 align-content: center; 239 310 justify-content: center; 240 311 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); } 243 334 } 244 335 a.button.superfrete-shipping-calculator-button { 245 336 width: 100%; 246 padding: 10px 0px;337 padding: var(--superfrete-spacing-sm) 0; 247 338 } 248 339 … … 252 343 /* Estilização da mensagem de alerta */ 253 344 .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); 257 348 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); 262 353 } 263 354 … … 265 356 .superfrete-container { 266 357 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); 271 362 max-width: 400px; 272 margin: 20pxauto;273 font-family: Arial, sans-serif;363 margin: var(--superfrete-spacing-lg) auto; 364 font-family: var(--superfrete-font-family); 274 365 } 275 366 … … 277 368 .superfrete-shipping-calculator-button { 278 369 display: block; 279 background: #0fae79;280 color: #fff;370 background: var(--superfrete-primary-color); 371 color: white; 281 372 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); 285 376 text-decoration: none; 286 377 transition: background 0.3s ease-in-out; 287 font-weight: bold;378 font-weight: var(--superfrete-font-weight-bold); 288 379 } 289 380 290 381 .superfrete-shipping-calculator-button:hover { 291 background: #EA961F;382 background: var(--superfrete-secondary-color); 292 383 } 293 384 … … 295 386 .superfrete-shipping-calculator-form { 296 387 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); 302 393 } 303 394 304 395 /* Mensagem de erro */ 305 396 .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); 311 402 text-align: center; 312 font-weight: bold;403 font-weight: var(--superfrete-font-weight-bold); 313 404 } 314 405 … … 317 408 .superfrete-woocommerce-shipping-calculator input { 318 409 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); 324 415 outline: none; 325 416 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); 326 420 } 327 421 328 422 .superfrete-woocommerce-shipping-calculator input:focus, 329 423 .superfrete-woocommerce-shipping-calculator select:focus { 330 border-color: #0fae79;424 border-color: var(--superfrete-primary-color); 331 425 } 332 426 333 427 /* Botão de calcular */ 334 428 .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); 343 437 cursor: pointer; 344 438 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); 346 441 } 347 442 348 443 .superfrete-update-address-button:hover { 349 background: #EA961F;444 background: var(--superfrete-secondary-color); 350 445 } 351 446 … … 363 458 } 364 459 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 5 5 Tested up to: 6.7 6 6 Requires PHP: 7.4 7 Stable tag: 2.1.67 Stable tag: 3.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 71 71 72 72 == 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 73 83 = 2.0 = 74 84 * Melhorias na interface administrativa para configuração do frete. -
superfrete/trunk/superfrete.php
r3289983 r3341621 3 3 Plugin Name: SuperFrete 4 4 Description: Plugin that provides integration with the SuperFrete platform. 5 Version: 2.1.65 Version: 3.3.0 6 6 Author: Super Frete 7 Author URI: https:// zafarie.com.br/7 Author URI: https://superfrete.com/ 8 8 Text Domain: superfrete 9 9 License: GPLv2 or later 10 10 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 11 16 */ 12 17 if (!defined('ABSPATH')) { … … 14 19 } 15 20 21 16 22 // Inclui a classe principal do plugin 17 23 include_once __DIR__ . '/app/App.php'; 18 24 19 25 // Inicializa o plugin 20 new SuperFrete \App();26 new SuperFrete_API\App(); 21 27 22 28 23 add_filter('plugin_action_links_' . plugin_basename(__FILE__), ['SuperFrete\App', 'superfrete_add_settings_link']); 29 add_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+) 33 add_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 16 16 */ 17 17 defined('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 = ''; 26 foreach ($calculator_attributes as $key => $value) { 27 $attributes_string .= sprintf(' %s="%s"', esc_attr($key), esc_attr($value)); 23 28 } 24 29 ?> 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> 29 49 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…', '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…', '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 124 59 <?php if (!empty($product_id)): ?> 125 60 <input type="hidden" name="product_id" value="<?php echo esc_attr($product_id); ?>"> … … 134 69 </div> 135 70 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> 137 78 </div>
Note: See TracChangeset
for help on using the changeset viewer.