All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m40s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 53s
- Buttons: add to cart (bag with plus), go to payment (lock), remove from the cart (bin). - Delivery: a pin for pickup in Franca, a truck for home delivery. - Payment: card and QR code on the methods, copy on the PIX code, a clock on its countdown, a check on the confirmation. - Status: a check when the files are sent, check, alert, redo and cross in the quality list instead of Unicode symbols, and a check on finished steps. - Kanban: a truck or pin for where an order goes, a speech bubble on the WhatsApp link, a download arrow on the file buttons. All Lucide shapes drawn inline, as in the header. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
143 lines
6.5 KiB
JavaScript
143 lines
6.5 KiB
JavaScript
/* Site DTF — moving between the Site's pages (addresses in site-pages.js), the
|
|
order progress bar, the product card prices and the phone total bar. It only
|
|
switches which page is shown and reads the page's state (the open product,
|
|
the cart, the checkout status); orders, prices and files stay with the other
|
|
site-*.js files. */
|
|
// Card prices come from the same table the checkout uses, so they cannot drift.
|
|
document.querySelectorAll('[data-de]').forEach(el=>{ el.textContent=rs(pisoEscada(el.dataset.de)); });
|
|
document.querySelectorAll('[data-ate]').forEach(el=>{ el.textContent=rs(TABELA[el.dataset.ate]); });
|
|
|
|
const TITULO_INICIO=document.title;
|
|
function tituloPagina(){
|
|
const p=paginaDe(location.pathname);
|
|
if(p.pagina==='carrinho') return 'Carrinho · DTF Dropstar';
|
|
if(p.pagina==='pagamento') return 'Pagamento · DTF Dropstar';
|
|
if(p.pagina==='pix') return 'Pagamento com PIX · DTF Dropstar';
|
|
if(p.pagina==='produto') return MODOS[p.modo].tit+' · DTF Dropstar';
|
|
return TITULO_INICIO;
|
|
}
|
|
|
|
// Shows the page for the current address; `ancora` scrolls to a section,
|
|
// otherwise the page opens at `y` (the saved position on Back and Forward).
|
|
function mostraPagina(ancora,y,suave){
|
|
const p=paginaDe(location.pathname);
|
|
if(p.pagina==='produto' && modo!==p.modo) abrir(p.modo);
|
|
document.documentElement.dataset.rota=p.pagina;
|
|
$('cards').style.display='';
|
|
document.title=tituloPagina();
|
|
window.dispatchEvent(new Event('dtf-page-changed'));
|
|
const el=ancora && document.getElementById(ancora);
|
|
if(el) el.scrollIntoView({behavior:suave?'smooth':'auto',block:'start'});
|
|
else window.scrollTo(0,y||0);
|
|
}
|
|
function vaiPara(url){
|
|
const u=new URL(url,location.href);
|
|
const ancora=u.hash.slice(1)||null;
|
|
if(u.pathname+u.search===location.pathname+location.search){
|
|
mostraPagina(ancora,0,true);
|
|
return;
|
|
}
|
|
try{ history.replaceState({...(history.state||{}),y:scrollY},''); }catch(e){}
|
|
history.pushState({y:0},'',u.pathname+u.search+u.hash);
|
|
mostraPagina(ancora,0,false);
|
|
}
|
|
|
|
(function(){
|
|
if('scrollRestoration' in history) history.scrollRestoration='manual';
|
|
addEventListener('popstate',e=>mostraPagina(e.state?null:location.hash.slice(1)||null,e.state?.y));
|
|
|
|
// Links to the Site's own pages move inside this document; everything else
|
|
// (the account portal, the store, e-mail) is a normal link.
|
|
document.addEventListener('click',e=>{
|
|
const a=e.target.closest && e.target.closest('a[href]');
|
|
if(!a || e.defaultPrevented || e.button || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || a.target) return;
|
|
const u=new URL(a.href,location.href);
|
|
if(u.origin!==location.origin) return;
|
|
const caminho=u.pathname.replace(/\/+$/,'')||'/';
|
|
if(caminho!=='/' && caminho!=='/index.html' && paginaDe(caminho).pagina==='inicio') return;
|
|
e.preventDefault(); vaiPara(u.pathname+u.search+u.hash);
|
|
});
|
|
|
|
const passos=$('passos'), barra=$('barraM'), vazio=$('carrVazio');
|
|
const foco=$('foco'), carr=$('carr'), status=$('checkoutStatus'), acoes=$('checkoutActions');
|
|
const rota=()=>document.documentElement.dataset.rota;
|
|
const temCheckout=()=>!!(status.textContent||'').trim() || acoes.children.length>0;
|
|
|
|
// The store header is sticky; the progress bar and the side panels sit below it.
|
|
function alturas(){
|
|
const topo=document.querySelector('.topo')?.offsetHeight||0;
|
|
document.documentElement.style.setProperty('--topo',topo+'px');
|
|
document.documentElement.style.setProperty('--topo-passos',(topo+(passos.hidden?0:passos.offsetHeight)+16)+'px');
|
|
}
|
|
function naTela(el){
|
|
const r=el.getBoundingClientRect();
|
|
return r.height>0 && r.top<innerHeight && r.bottom>0;
|
|
}
|
|
// Where the phone bar's button leads: the add-to-cart button on a product
|
|
// page, the order summary in the cart.
|
|
function alvoBarra(){
|
|
if(rota()==='produto' && itemAtual) return $('bAdd');
|
|
if(rota()==='carrinho' && (pedido.length || itemAtual) && !temCheckout())
|
|
return document.querySelector('.resumo');
|
|
return null;
|
|
}
|
|
let pendente=false;
|
|
function pinta(){
|
|
pendente=false;
|
|
const r=rota();
|
|
// With nothing in it, the cart page says so instead of showing an empty form.
|
|
const semItens = !pedido.length && !itemAtual;
|
|
const carrinhoVazio = r==='carrinho' && semItens && !temCheckout();
|
|
document.documentElement.toggleAttribute('data-sem-itens', semItens);
|
|
vazio.hidden = !carrinhoVazio;
|
|
passos.hidden = r==='inicio' || carrinhoVazio;
|
|
alturas();
|
|
if(!passos.hidden){
|
|
const atual = r==='produto' ? 1 : r==='pagamento' || r==='pix' ? 3 : 2;
|
|
passos.querySelectorAll('[data-passo]').forEach(a=>{
|
|
const n=Number(a.dataset.passo);
|
|
a.classList.toggle('atual',n===atual); a.classList.toggle('feito',n<atual);
|
|
const marca=a.querySelector('span');
|
|
if(marca) marca.innerHTML = n<atual ? icone('ok') : String(n);
|
|
if(n===atual) a.setAttribute('aria-current','step'); else a.removeAttribute('aria-current');
|
|
});
|
|
passos.querySelector('[data-passo="1"]').href =
|
|
modo && foco.classList.contains('on') ? PAGINAS[modo] : '/#envio';
|
|
}
|
|
const alvo=alvoBarra();
|
|
const mostrar=!!alvo && !naTela(alvo);
|
|
barra.hidden=!mostrar;
|
|
if(mostrar){
|
|
$('barraMTot').textContent=$('carrTot').textContent;
|
|
$('barraMSub').textContent= r==='produto' ? 'Total até agora' : 'Seu pedido';
|
|
}
|
|
}
|
|
function agenda(){ if(!pendente){ pendente=true; requestAnimationFrame(pinta); } }
|
|
|
|
const obs=new MutationObserver(agenda);
|
|
obs.observe(foco,{attributes:true,attributeFilter:['class']});
|
|
obs.observe(carr,{attributes:true,attributeFilter:['class']});
|
|
obs.observe($('prev'),{attributes:true,attributeFilter:['class']});
|
|
obs.observe(status,{childList:true,characterData:true,subtree:true});
|
|
obs.observe(acoes,{childList:true});
|
|
obs.observe($('carrTot'),{childList:true,characterData:true,subtree:true});
|
|
addEventListener('scroll',agenda,{passive:true});
|
|
addEventListener('resize',agenda);
|
|
addEventListener('dtf-cart-changed',agenda);
|
|
addEventListener('dtf-page-changed',agenda);
|
|
|
|
// Payment is a step only once the order has been sent.
|
|
passos.querySelector('[data-passo="3"]').addEventListener('click',e=>{
|
|
let enviado=false;
|
|
try{ enviado=!!localStorage.getItem('dtf-quote'); }catch(_){}
|
|
if(!enviado){ e.preventDefault(); e.stopPropagation(); }
|
|
},true);
|
|
$('barraMBtn').addEventListener('click',e=>{
|
|
e.preventDefault();
|
|
alvoBarra()?.scrollIntoView({behavior:'smooth',block:'center'});
|
|
});
|
|
|
|
try{ history.replaceState({...(history.state||{}),y:0},''); }catch(e){}
|
|
mostraPagina(location.hash.slice(1)||null,0);
|
|
})();
|