feat: redesign the Site order flow and fix the cart layout
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 2m23s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m48s
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 2m23s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m48s
New landing (hero with a sheet preview, four steps, product cards priced from the checkout table, price table and benefits), a progress bar that follows the order through Montagem, Dados e entrega and Pagamento, the live sheet beside the artworks, and a running total bar on phones. The cart's saved-in-browser note no longer takes a grid column, which had pushed the order into a narrow strip and the summary below it. The previous look is kept in tag ui-v1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
72
web/site-steps.js
Normal file
72
web/site-steps.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/* Site DTF — the order progress bar, the product card prices and the phone
|
||||
total bar. This file only reads the page's state (which panels are open,
|
||||
the cart total, the checkout status); it never changes an order, a price or
|
||||
a file, so the flow in the other site-*.js files behaves exactly as before. */
|
||||
(function(){
|
||||
// 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 passos=$('passos'), barra=$('barraM');
|
||||
// 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');
|
||||
}
|
||||
const foco=$('foco'), carr=$('carr'), status=$('checkoutStatus'), acoes=$('checkoutActions');
|
||||
|
||||
function naTela(el){
|
||||
const r=el.getBoundingClientRect();
|
||||
return r.top < innerHeight*0.55 && r.bottom > innerHeight*0.2;
|
||||
}
|
||||
function etapa(){
|
||||
// Once the cart is sent, the quote, the payment and the order live in the
|
||||
// checkout status: that is the payment step.
|
||||
if((status.textContent||'').trim() || acoes.children.length) return 3;
|
||||
if(carr.classList.contains('on') && (naTela(carr) || !foco.classList.contains('on'))) return 2;
|
||||
return 1;
|
||||
}
|
||||
let pendente=false;
|
||||
function pinta(){
|
||||
pendente=false;
|
||||
const ativo=foco.classList.contains('on') || carr.classList.contains('on') || (status.textContent||'').trim();
|
||||
passos.hidden=!ativo; alturas();
|
||||
if(ativo){
|
||||
const atual=etapa();
|
||||
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');
|
||||
});
|
||||
}
|
||||
// Phone bar: the running total while the cart itself is off screen.
|
||||
const temPedido=carr.classList.contains('on');
|
||||
const mostrar=temPedido && !naTela(carr) && !(status.textContent||'').trim();
|
||||
barra.hidden=!mostrar;
|
||||
if(mostrar){
|
||||
$('barraMTot').textContent=$('carrTot').textContent;
|
||||
$('barraMSub').textContent=foco.classList.contains('on')?'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(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);
|
||||
|
||||
// Steps are links; step 3 only makes sense once there is something to pay.
|
||||
passos.querySelectorAll('a').forEach(a=>a.addEventListener('click',e=>{
|
||||
const destino=document.querySelector(a.getAttribute('href'));
|
||||
if(!destino || destino.offsetParent===null){ e.preventDefault(); return; }
|
||||
e.preventDefault(); destino.scrollIntoView({behavior:'smooth',block:'start'});
|
||||
}));
|
||||
$('barraMBtn').addEventListener('click',e=>{ e.preventDefault(); carr.scrollIntoView({behavior:'smooth',block:'start'}); });
|
||||
pinta();
|
||||
})();
|
||||
Reference in New Issue
Block a user