feat: rebuild the Site product page around a buy box
All checks were successful
Build and deploy / Validate source (push) Successful in 10s
Build and deploy / Integration suite on a real stack (push) Successful in 2m46s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m46s

Artworks on the left; on the right a box that stays in view with the live
sheet, the grade, the price per metre, the metres charged, the total, the
resolution note and "Adicionar ao carrinho". The separate quality and
preview panels, the second sheet preview, the per-row mini sheets, the
summary box and the repeated findings list are gone: each piece of
information now appears once.

Each artwork row carries at most one hint (resolution first, otherwise a
width that saves film), the ready-sheet/loose-artwork choice is a toggle
beside the title, the upload area is one bar and the tips are collapsed.
The box only shows the item the page already priced, so it always matches
the cart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-24 18:02:45 -03:00
parent b81ff8d03d
commit cbbbdb351a
6 changed files with 230 additions and 147 deletions

View File

@@ -319,36 +319,6 @@ function sugestao(a){
if(economiaCm<0.5) return null; // menos de meio centímetro não é notícia
return {alvo, de:n, para:n+1, fileiras:fa-fd, cm:economiaCm};
}
// A cota em escala: o cliente ve a peca dentro da largura do filme, quantas cabem
// lado a lado e quanto sobra. Numero em texto ninguem le; retangulo todo mundo entende.
function vizPeca(a){
const BASE=300, L=larguraFilme();
const e0=BASE/L, alt0=a.cm*propDe(a)*e0;
const k = alt0>132 ? 132/alt0 : 1; // tira nunca fica alta demais
const e = e0*k;
const n=cabemNaLinha(a.cm), pw=a.cm*e, ph=a.cm*propDe(a)*e, gp=GAP_CM*e;
const sobra=L-(n*a.cm+(n-1)*GAP_CM);
const ext=escapeHTML(a.f.name.split('.').pop().toUpperCase());
let pecas='';
for(let i=0;i<n;i++) pecas+='<div class="pc" style="width:'+pw.toFixed(1)+'px;height:'+
ph.toFixed(1)+'px">'+(a.src?'<img src="'+a.src+'" alt="" style="transform:'+
(a.giro?'rotate(90deg) ':'')+(a.esp?'scaleX(-1)':'')+'">':'<span>'+ext+'</span>')+'</div>';
const Wt=(L*e+6).toFixed(1); // +6 = borda e respiro da tira
return '<div class="viz">'+
'<div class="vizRow">'+
'<div class="vizH" style="height:'+ph.toFixed(1)+'px"><span>'+n1(a.cm*propDe(a))+' cm</span></div>'+
'<div class="vizTira" style="width:'+Wt+'px">'+
'<div class="vizL"><i></i><b>'+n1(L)+' cm de filme</b><i></i></div>'+
'<div class="tira" style="height:'+ph.toFixed(1)+'px;gap:'+gp.toFixed(1)+'px">'+pecas+
(sobra>0.25?'<div class="folga"><span>sobra '+n1(sobra)+' cm</span></div>':'')+
'</div>'+
'</div>'+
'</div>'+
'<div class="vizPe"><b>'+n1(a.cm)+' cm</b> de largura · <b>'+n+
(n>1?' peças':' peça')+'</b> por fileira'+
(sobra>0.25?' · sobram <b>'+n1(sobra)+' cm</b>':' · largura toda aproveitada')+'</div>'+
'</div>';
}
const n1=v=>v.toFixed(1).replace('.',',');
function pintaArtes(){
@@ -362,17 +332,20 @@ function pintaArtes(){
}else if(a.cm>0 && a.src){
const dpi=Math.round(dpiDe(a)), n=Math.max(6,Math.min(100,Math.round(dpi/300*100)));
barra='<div class="qb2"><i class="'+cls(n)+'" style="width:'+n+'%"></i></div>';
const fila=cabemNaLinha(a.cm);
calc='<span class="'+cls(n)+'"><b>'+n+'%</b> · '+dpi+' DPI</span><span>'+
a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm</span>';
a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm · '+fila+' por fileira</span>';
const sug=sugestao(a);
dica = sug
? '<div class="dica"><b>Reduzindo para '+n1(sug.alvo)+' cm você economiza '+
n1(sug.cm)+' cm de folha</b>Passam de '+sug.de+' para '+sug.para+
' por fileira, e as suas '+a.q+' peças ocupam '+sug.fileiras+
' fileira'+(sug.fileiras>1?'s':'')+' a menos.<button data-usar="'+i+'" data-cm="'+
sug.alvo+'">Usar sugerido</button></div>'
// One hint per row, on the row it is about: resolution first, since it
// changes the price; otherwise a width that saves sheet.
const maxCm=Math.floor(a.px/DPI_AVISA*2.54*10)/10;
dica = dpi<DPI_AVISA && maxCm>0 && maxCm<a.cm
? '<div class="dica av">Pequena para '+n1(a.cm)+' cm. Em '+DPI_AVISA+' DPI ela sai com até '+
n1(maxCm)+' cm, ou envie um arquivo maior.</div>'
: sug
? '<div class="dica">Com '+n1(sug.alvo)+' cm cabem '+sug.para+' por fileira e a folha encolhe '+
n1(sug.cm)+' cm.<button data-usar="'+i+'" data-cm="'+sug.alvo+'">Usar '+n1(sug.alvo)+' cm</button></div>'
: '';
dica = vizPeca(a) + dica;
}
return '<div class="art'+(a===recemChegada?' nova':'')+'"><div class="l1"><b>'+escapeHTML(a.f.name)+'</b>'+
'<span style="color:var(--fraco);font-size:10.5px">'+