Files
dtf-system/web/site-steps.js
Cauê Faleiros 43043c361c
All checks were successful
Build and deploy / Validate source (push) Successful in 6s
Build and deploy / Integration suite on a real stack (push) Successful in 2m12s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m35s
feat: give payment its own page
The PIX and card choices appeared under the cart, on the same page as the
customer's details. "Ir para o pagamento" now sends the order and opens
/pagamento, step 3 of the progress bar: the server's order summary, then PIX
or card, each opening below. The cart keeps only the sending progress and its
errors; a changed cart is sent again instead of offering the old quote.
Portal links open the payment page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:45:44 -03:00

140 lines
6.3 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==='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' ? 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);
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);
})();