Faça abaixo sua cotação e contrate um serviço de qualidade e eficiência
/* Namespace específico para evitar conflitos no WordPress */
.wf-cotador-container{–wf-bg:#0b0f14;–wf-card:#121a24;–wf-muted:#8aa0b2;–wf-text:#e8f0f7;–wf-primary:#1ecde2;–wf-accent:#7cf29c;–wf-danger:#ff6b6b;–wf-border:#1f2a36;–wf-success:#48d46a}
.wf-cotador-container *{box-sizing:border-box}
.wf-cotador-container .wf-brand{display:flex;gap:12px;align-items:center;margin-bottom:14px}
.wf-cotador-container .wf-logo{width:40px;height:40px;border-radius:12px;background:radial-gradient(circle at 30% 30%,var(–wf-primary),#3fe6b9);box-shadow:0 0 0 4px rgba(30,205,226,.12)}
.wf-cotador-container h1{font-size:24px;margin:0;color:var(–wf-text)}
.wf-cotador-container .wf-sub{color:var(–wf-muted);margin:4px 0 22px;font-size:14px;line-height:1.5}
.wf-cotador-container .wf-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:18px}
/* Responsividade melhorada */
@media (max-width:1000px){.wf-cotador-container .wf-grid{grid-template-columns:1fr}}
@media (max-width:768px){
.wf-cotador-container{padding:0 12px;margin:20px auto}
.wf-cotador-container .wf-grid{gap:12px}
.wf-cotador-container h1{font-size:20px}
}
@media (max-width:480px){
.wf-cotador-container .wf-row{grid-template-columns:1fr}
.wf-cotador-container .wf-row3{grid-template-columns:1fr}
}
.wf-cotador-container .wf-card{background:linear-gradient(180deg,rgba(18,26,36,.96),rgba(18,26,36,.88));border:1px solid var(–wf-border);border-radius:16px;box-shadow:0 20px 40px rgba(0,0,0,.35);padding:18px}
.wf-cotador-container .wf-card h2{font-size:18px;margin:0 0 12px;color:var(–wf-text)}
.wf-cotador-container fieldset{border:none;margin:0;padding:0}
.wf-cotador-container .wf-row{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:10px}
.wf-cotador-container .wf-row3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:10px}
.wf-cotador-container .wf-full{grid-column:1/-1}
/* Labels melhorados para acessibilidade */
.wf-cotador-container label{display:block;font-size:12px;color:var(–wf-muted);margin-bottom:6px;font-weight:500}
.wf-cotador-container .wf-required::after{content:” *”;color:var(–wf-danger)}
/* Inputs melhorados */
.wf-cotador-container input,.wf-cotador-container select,.wf-cotador-container button,.wf-cotador-container textarea{
width:100%;padding:12px;border-radius:12px;border:1px solid var(–wf-border);
background:#0f1620;color:#e8f0f7;outline:none;font-size:14px;transition:all 0.2s ease
}
.wf-cotador-container input:focus,.wf-cotador-container select:focus,.wf-cotador-container textarea:focus{
border-color:var(–wf-primary);box-shadow:0 0 0 3px rgba(30,205,226,.12)
}
.wf-cotador-container input:invalid{border-color:var(–wf-danger)}
.wf-cotador-container input[aria-invalid=”true”]{border-color:var(–wf-danger)}
/* Estados de loading */
.wf-cotador-container .wf-loading{opacity:0.6;pointer-events:none}
.wf-cotador-container .wf-spinner{
display:inline-block;width:16px;height:16px;border:2px solid var(–wf-muted);
border-top:2px solid var(–wf-primary);border-radius:50%;animation:wf-spin 1s linear infinite
}
@keyframes wf-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
.wf-cotador-container .wf-inline{display:flex;gap:10px;align-items:center}
.wf-cotador-container .wf-muted{color:var(–wf-muted);font-size:12px}
.wf-cotador-container .wf-badge{display:inline-flex;gap:6px;align-items:center;background:#0f1620;border:1px solid var(–wf-border);border-radius:999px;padding:6px 10px;font-size:12px;color:var(–wf-muted)}
/* Botões melhorados */
.wf-cotador-container .wf-btn{
cursor:pointer;border:1px solid var(–wf-border);background:linear-gradient(180deg,#122030,#0e1823);
padding:12px;border-radius:12px;font-size:14px;font-weight:500;transition:all 0.2s ease;
position:relative;overflow:hidden
}
.wf-cotador-container .wf-btn:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.2)}
.wf-cotador-container .wf-btn.wf-primary{
text-transform:uppercase;background:linear-gradient(180deg,#1ecde2,#15b2c5);
color:#001018;font-weight:700;box-shadow:0 10px 24px rgba(30,205,226,.35)
}
.wf-cotador-container .wf-btn.wf-success{
background:linear-gradient(180deg,#7cf29c,#48d46a);color:#0a1b11;font-weight:700
}
.wf-cotador-container .wf-btn.wf-ghost{background:#0f1620}
.wf-cotador-container .wf-btn:disabled{opacity:.6;cursor:not-allowed;transform:none}
.wf-cotador-container .wf-btn:focus{outline:2px solid var(–wf-primary);outline-offset:2px}
.wf-cotador-container .wf-aside{display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.wf-cotador-container .wf-hr{height:1px;background:var(–wf-border);margin:14px 0}
.wf-cotador-container .wf-result .wf-line{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px dashed rgba(138,160,178,.18)}
.wf-cotador-container .wf-result .wf-line strong{font-variant-numeric:tabular-nums}
.wf-cotador-container .wf-total{display:flex;justify-content:space-between;align-items:center;padding:12px;border:1px solid var(–wf-border);border-radius:14px;background:linear-gradient(180deg,#121d29,#101821);font-size:18px;font-weight:800}
.wf-cotador-container .wf-pill{padding:6px 10px;border-radius:999px;background:#0f1620;border:1px solid var(–wf-border)}
.wf-cotador-container .wf-warn{color:var(–wf-danger)}
.wf-cotador-container .wf-ok{color:var(–wf-accent)}
.wf-cotador-container .wf-tiny{font-size:11px;color:var(–wf-muted)}
.wf-cotador-container .wf-hidden{display:none}
/* Mensagens de erro */
.wf-cotador-container .wf-error-message{
background:rgba(255,107,107,.1);border:1px solid var(–wf-danger);
color:var(–wf-danger);padding:8px 12px;border-radius:8px;font-size:12px;margin-top:4px
}
/* Toast melhorado */
.wf-toast{
position:fixed;left:50%;bottom:30px;transform:translateX(-50%);
background:#0f1620;border:1px solid var(–wf-border);padding:10px 14px;
border-radius:12px;color:#e8f0f7;box-shadow:0 10px 24px rgba(0,0,0,.35);
display:none;z-index:9999;max-width:90vw;text-align:center
}
/* Melhorias para screen readers */
.wf-cotador-container .wf-sr-only{
position:absolute;width:1px;height:1px;padding:0;margin:-1px;
overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0
}
(function(){
‘use strict’;
// =================== CONFIG ===================
const CONFIG = {
brand: “WF TRANSPORTES EXPRESS”,
locale: “pt-BR”,
currency: “BRL”,
roundTo: 0.5,
// Regras comerciais
minimoAte6km: 12.00,
precoKmAte60cm2: 2.45,
precoKmAcima60cm2: 3.00,
areaLimiteCm2: 60,
volumeMaxCm3: 1500000, // 1,5 m³ total em cm³
expressAdd: 30.00,
// Origens (SP)
origens: [“São José do Rio Preto”,”Bady Bassitt”,”Ipiguá”,”Mirassol”,”Mirassolândia”,”Onda Verde”],
origemBaseIsenta: “São José do Rio Preto”,
origemColetaFee: 20.00,
// Destinos (SP) — ORDEM ALFABÉTICA
destinos: [
“Araçatuba”,”Bady Bassitt”,”Barretos”,”Bálsamo”,”Bebedouro”,”Birigui”,”Buritama”,”Catanduva”,”Catiguá”,”Cedral”,
“Fernandópolis”,”Gastão Vidigal”,”Guaíra”,”Guzolândia”,”Ibirá”,”Ipiguá”,”Itajobi”,”Jaci”,”Jales”,”José Bonifácio”,
“Macaubal”,”Magda”,”Miguelópolis”,”Mirassol”,”Mirassolândia”,”Monte Aprazível”,”Neves Paulista”,”Nhandeara”,”Novais”,
“Novo Horizonte”,”Olímpia”,”Onda Verde”,”Penápolis”,”Potirendaba”,”São José do Rio Preto”,”Severínia”,”Tabapuã”,
“Tanabi”,”Talhados”,”Uchoa”,”Votuporanga”
],
// IMPORTANTE: Configurações sensíveis movidas para o backend
// Estas configurações devem ser implementadas no servidor WordPress
apiEndpoints: {
calculateDistance: ‘/wp-json/wf-frete/v1/calculate-distance’,
createPixPayment: ‘/wp-json/wf-frete/v1/create-pix’,
createCardPayment: ‘/wp-json/wf-frete/v1/create-card-payment’
}
};
// =============== Helpers ===============
const $ = (s) => document.querySelector(s);
const $$ = (s) => document.querySelectorAll(s);
const money = (v) => (isFinite(v) ? v : 0).toLocaleString(CONFIG.locale, {
style: ‘currency’,
currency: CONFIG.currency
});
const num = (el) => {
const value = (el?.value || ”).toString().replace(‘,’, ‘.’);
const parsed = parseFloat(value);
return isFinite(parsed) ? parsed : 0;
};
const stepRound = (n, step = CONFIG.roundTo) => Math.round(n / step) * step;
const toast = (message, type = ‘info’) => {
const toastEl = $(‘#wf-cotador-toast’);
toastEl.textContent = message;
toastEl.className = `wf-toast wf-toast-${type}`;
toastEl.style.display = ‘block’;
setTimeout(() => {
toastEl.style.display = ‘none’;
}, 4000);
};
// =============== Validação ===============
const validators = {
required: (value, fieldName) => {
if (!value || value.trim() === ”) {
return `${fieldName} é obrigatório`;
}
return null;
},
email: (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (value && !emailRegex.test(value)) {
return ‘E-mail inválido’;
}
return null;
},
phone: (value) => {
const phoneRegex = /^\([0-9]{2}\)\s[0-9]\s[0-9]{4}-[0-9]{4}$/;
if (value && !phoneRegex.test(value)) {
return ‘Formato inválido. Use: (17) 9 9999-9999’;
}
return null;
},
number: (value, min, max, fieldName) => {
const n = parseFloat(value);
if (isNaN(n)) {
return `${fieldName} deve ser um número válido`;
}
if (min !== undefined && n max) {
return `${fieldName} deve ser menor que ${max}`;
}
return null;
}
};
function validateField(field) {
const id = field.id;
const value = field.value.trim();
const errorEl = $(`#${id.replace(‘wf-cotador-‘, ‘wf-‘)}-error`);
let error = null;
// Limpar erro anterior
if (errorEl) {
errorEl.textContent = ”;
errorEl.classList.add(‘wf-hidden’);
field.setAttribute(‘aria-invalid’, ‘false’);
}
// Validações específicas por campo
switch (id) {
case ‘wf-cotador-origem’:
case ‘wf-cotador-destino’:
error = validators.required(value, field.previousElementSibling.textContent.replace(‘ *’, ”));
break;
case ‘wf-cotador-comp’:
case ‘wf-cotador-larg’:
case ‘wf-cotador-alt’:
error = validators.required(value, field.previousElementSibling.textContent.replace(‘ *’, ”));
if (!error) {
error = validators.number(value, 1, 200, field.previousElementSibling.textContent.replace(‘ *’, ”));
}
break;
case ‘wf-cotador-qty’:
error = validators.required(value, ‘Quantidade’);
if (!error) {
error = validators.number(value, 1, 100, ‘Quantidade’);
}
break;
case ‘wf-cotador-nome’:
error = validators.required(value, ‘Nome’);
if (!error && value.length {
if (!validateField(field)) {
isValid = false;
}
});
return isValid;
}
// =============== UI / Campos ===============
function fillSelects() {
const origemSelect = $(‘#wf-cotador-origem’);
const destinoSelect = $(‘#wf-cotador-destino’);
origemSelect.innerHTML = ‘Selecione a origem’ +
CONFIG.origens.map(c => `${c}`).join(”);
destinoSelect.innerHTML = ‘Selecione o destino’ +
CONFIG.destinos.map(c => `${c}`).join(”);
}
// =============== Cálculo de Distância (via Backend) ===============
let distanceTimeout;
async function autoDistance() {
// Debounce para evitar muitas chamadas
clearTimeout(distanceTimeout);
distanceTimeout = setTimeout(async () => {
const origem = $(‘#wf-cotador-origem’).value;
const destino = $(‘#wf-cotador-destino’).value;
const endOrigem = $(‘#wf-cotador-endO’).value.trim();
const endDestino = $(‘#wf-cotador-endD’).value.trim();
if (!origem || !destino) {
$(‘#wf-cotador-badge’).textContent = ‘Distância: 0,0 km’;
$(‘#wf-cotador-km’).dataset.value = ‘0’;
calcular();
return;
}
try {
$(‘#wf-cotador-badge’).innerHTML = ‘Distância: calculando…’;
// Chamada para o backend WordPress
const response = await fetch(CONFIG.apiEndpoints.calculateDistance, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
‘X-WP-Nonce’: window.wfFreteNonce || ” // Nonce do WordPress para segurança
},
body: JSON.stringify({
origem: endOrigem ? `${endOrigem}, ${origem}, SP, Brasil` : `${origem}, SP, Brasil`,
destino: endDestino ? `${endDestino}, ${destino}, SP, Brasil` : `${destino}, SP, Brasil`
})
});
if (!response.ok) {
throw new Error(‘Erro na API’);
}
const data = await response.json();
const km = data.distance || 0;
$(‘#wf-cotador-km’).dataset.value = String(km);
$(‘#wf-cotador-badge’).textContent = `Distância: ${km.toFixed(1)} km`;
} catch (error) {
console.error(‘Erro ao calcular distância:’, error);
$(‘#wf-cotador-km’).dataset.value = ‘0’;
$(‘#wf-cotador-badge’).textContent = ‘Distância: erro ao calcular’;
toast(‘Erro ao calcular distância. Usando cálculo aproximado.’, ‘warning’);
}
calcular();
}, 500);
}
// =============== Cálculo ===============
function areaItem() {
return Math.max(0, num($(‘#wf-cotador-comp’))) * Math.max(0, num($(‘#wf-cotador-larg’)));
}
function volumeTotal() {
const c = num($(‘#wf-cotador-comp’));
const l = num($(‘#wf-cotador-larg’));
const a = num($(‘#wf-cotador-alt’));
const q = Math.max(1, parseInt($(‘#wf-cotador-qty’).value || ‘1’, 10));
return (c && l && a) ? c * l * a * q : 0;
}
function calcular() {
const km = parseFloat($(‘#wf-cotador-km’).dataset.value || ‘0’) || 0;
const area = areaItem();
const vol = volumeTotal();
const ped = Math.max(0, num($(‘#wf-cotador-ped’)));
const serv = $(‘#wf-cotador-serv’).value;
const origem = $(‘#wf-cotador-origem’).value;
const destino = $(‘#wf-cotador-destino’).value;
const alertas = [];
if (vol > CONFIG.volumeMaxCm3) {
alertas.push(‘Volume total excede 1,5 m³.’);
}
let base = 0;
let regra = ”;
if (km > 0 && km 6) {
const pk = (area > CONFIG.areaLimiteCm2) ? CONFIG.precoKmAcima60cm2 : CONFIG.precoKmAte60cm2;
base = km * pk;
regra = `R$ ${pk.toFixed(2)}/km (${area > CONFIG.areaLimiteCm2 ? ‘>’ : ‘≤’} ${CONFIG.areaLimiteCm2} cm²)`;
}
const express = (serv === ‘express’) ? CONFIG.expressAdd : 0;
const coleta = (origem && origem !== CONFIG.origemBaseIsenta) ? CONFIG.origemColetaFee : 0;
let total = base + ped + express + coleta;
total = stepRound(total);
renderResumo({ km, origem, destino, area, vol, ped, express, coleta, base, regra, total });
const alertEl = $(‘#wf-cotador-alert’);
alertEl.textContent = alertas.join(‘ ‘);
// Habilitar botões de pagamento se houver valor
const pixBtn = $(‘#wf-cotador-pix’);
const cardBtn = $(‘#wf-cotador-card’);
if (total > 0 && origem && destino) {
pixBtn.disabled = false;
cardBtn.disabled = false;
} else {
pixBtn.disabled = true;
cardBtn.disabled = true;
}
return { total, alertas };
}
function renderResumo(d) {
const el = $(‘#wf-cotador-linhas’);
el.innerHTML = ”;
const add = (t, sub, val) => {
const div = document.createElement(‘div’);
div.className = ‘wf-line’;
div.innerHTML = `${t}${sub ? `WF TRANSPORTES EXPRESS — Cotador de Frete
O frete mínimo é R$ 12,00 até 6 km de distância, acima de 6 Km o frete é calculado R$ 2,45 por Km rodado para pacotes até 60 cm² (centímetros quadrados) ou R$ 3,00 o km rodado para pacotes acima de 60 cm² (centímetros quadrados) • Capacidade máxima de 1,5 m³ (metros cúbicos) • Sempre informar o valor total de pedágios no campo para pedágios quando houver.
Dados do envio
Origem (cidade — SP)
Selecione a origem
Destino (cidade — SP)
Selecione o destino
Endereço de origem (logradouro, nº, bairro)
Opcional: para cálculo mais preciso da distância
Endereço de destino (logradouro, nº, bairro)
Opcional: para cálculo mais preciso da distância
A distância é calculada em tempo real pelos endereços completos informados.
Comprimento (cm)
Largura (cm)
Altura (cm)
Quantidade de itens
Pedágios (R$ total)
Prazo
Padrão
EXPRESS 5H (+R$ 30,00)
Contato (WhatsApp)
Formato: (17) 9 9999-9999
Nome
E-mail
Observações (opcional)
Somente cidades listadas. Volume máximo total: 1,5 m³.
CALCULAR FRETE
Resumo
Distância: 0,0 km
Total
R$ 0,00
Frete contratado! Guarde o comprovante do pagamento.
Pagar com Pix
Pagar com Cartão
Pagamento Pix
Copia e cola Pix
Copie este código e cole no seu app bancário
Copiar código
Já paguei
Fechar
Após pagar, clique em “Já paguei”.
${sub}` : ”}${(val !== ” && val != null) ? money(val) : ”}`; el.appendChild(div); }; if (d.origem && d.destino) { add(‘Trajeto’, `${d.origem} → ${d.destino}`, ”); } if (d.area > 0) { add(‘Área (C×L por item)’, `${d.area.toFixed(0)} cm²`, ”); } if (d.vol > 0) { add(‘Volume total’, `${(d.vol / 1_000_000).toFixed(3)} m³`, ”); } if (d.regra && d.base > 0) { add(‘Regra aplicada’, d.regra, d.base); } if (d.ped > 0) { add(‘Pedágios’, ”, d.ped); } if (d.express > 0) { add(‘EXPRESS 5H’, ”, d.express); } if (d.coleta > 0) { add(‘Taxa de coleta’, ”, d.coleta); } $(‘#wf-cotador-total’).textContent = money(d.total); } // =============== Eventos =============== function setupEvents() { const form = $(‘#wf-cotador-form’); const calcBtn = $(‘#wf-cotador-calc’); // Validação em tempo real const inputs = $$(‘#wf-cotador-form input, #wf-cotador-form select’); inputs.forEach(input => { input.addEventListener(‘blur’, () => validateField(input)); input.addEventListener(‘input’, () => { // Remover erro ao digitar const errorEl = $(`#${input.id.replace(‘wf-cotador-‘, ‘wf-‘)}-error`); if (errorEl && !errorEl.classList.contains(‘wf-hidden’)) { validateField(input); } }); }); // Recalcular quando origem/destino/endereços mudarem [‘#wf-cotador-origem’, ‘#wf-cotador-destino’, ‘#wf-cotador-endO’, ‘#wf-cotador-endD’].forEach(sel => { const el = $(sel); if (el) { el.addEventListener(‘change’, autoDistance); el.addEventListener(‘input’, autoDistance); } }); // Recalcular quando dimensões mudarem [‘#wf-cotador-comp’, ‘#wf-cotador-larg’, ‘#wf-cotador-alt’, ‘#wf-cotador-qty’, ‘#wf-cotador-ped’, ‘#wf-cotador-serv’].forEach(sel => { const el = $(sel); if (el) { el.addEventListener(‘input’, calcular); el.addEventListener(‘change’, calcular); } }); // Submit do formulário form.addEventListener(‘submit’, (e) => { e.preventDefault(); if (!validateForm()) { toast(‘Por favor, corrija os erros no formulário’, ‘error’); return; } const result = calcular(); if (result.total <= 0) { toast('Preencha os dados para calcular o frete', 'warning'); return; } toast('Frete calculado com sucesso!', 'success'); }); // Botões de pagamento $('#wf-cotador-pix').addEventListener('click', handlePixPayment); $('#wf-cotador-card').addEventListener('click', handleCardPayment); // Botões do modal de pagamento $('#wf-cotador-copy').addEventListener('click', copyPixCode); $('#wf-cotador-paid').addEventListener('click', markAsPaid); $('#wf-cotador-close').addEventListener('click', closePayment); } // =============== Pagamentos =============== async function handlePixPayment() { if (!validateForm()) { toast('Complete todos os campos obrigatórios', 'error'); return; } const result = calcular(); if (result.total <= 0) { toast('Valor inválido para pagamento', 'error'); return; } try { // Chamada para backend criar PIX const response = await fetch(CONFIG.apiEndpoints.createPixPayment, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.wfFreteNonce || '' }, body: JSON.stringify({ valor: result.total, nome: $('#wf-cotador-nome').value, email: $('#wf-cotador-email').value, whatsapp: $('#wf-cotador-whats').value, origem: $('#wf-cotador-origem').value, destino: $('#wf-cotador-destino').value, observacoes: $('#wf-cotador-obs').value }) }); if (!response.ok) { throw new Error('Erro ao gerar PIX'); } const data = await response.json(); // Mostrar modal de pagamento $('#wf-cotador-qr').src = data.qrCodeUrl; $('#wf-cotador-pixcode').value = data.pixCode; $('#wf-cotador-pay').classList.remove('wf-hidden'); toast('PIX gerado com sucesso!', 'success'); } catch (error) { console.error('Erro ao gerar PIX:', error); toast('Erro ao gerar PIX. Tente novamente.', 'error'); } } async function handleCardPayment() { if (!validateForm()) { toast('Complete todos os campos obrigatórios', 'error'); return; } const result = calcular(); if (result.total { $(‘#wf-cotador-origem’).focus(); }, 100); } // Aguardar DOM carregar if (document.readyState === ‘loading’) { document.addEventListener(‘DOMContentLoaded’, init); } else { init(); } })();
