PluginProbe
SuperFrete / 3.3.2
SuperFrete v3.3.2
trunk 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4
superfrete / app / Controllers / Admin / SuperFrete_Settings.php

SuperFrete_Settings.php in SuperFrete 3.3.2, at app/Controllers/Admin/SuperFrete_Settings.php

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