From cbbbdb351a77169a169319fcb8130ad268dbbd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Thu, 24 Sep 2026 18:02:45 -0300 Subject: [PATCH] feat: rebuild the Site product page around a buy box 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 --- web/index.html | 181 ++++++++++++++++++++++++--------------------- web/site-compra.js | 30 ++++++++ web/site-flow.js | 4 +- web/site-pdf.js | 49 +++--------- web/site-steps.js | 7 +- web/site-v2.css | 106 +++++++++++++++++++++----- 6 files changed, 230 insertions(+), 147 deletions(-) create mode 100644 web/site-compra.js diff --git a/web/index.html b/web/index.html index 26a0bd7..86e9944 100644 --- a/web/index.html +++ b/web/index.html @@ -748,7 +748,7 @@ footer a:hover{color:var(--laranja2)} justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:11px;color:#5A6570} @media(prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto}} - + @@ -924,33 +924,33 @@ footer a:hover{color:var(--laranja2)}
- +

—

-
-
-
-
- - +
+
+
+
-
@@ -1072,16 +1089,9 @@ footer a:hover{color:var(--laranja2)}
-
-

Ressalva de resolução

-
- -
-
@@ -1279,6 +1289,7 @@ footer a:hover{color:var(--laranja2)} + diff --git a/web/site-compra.js b/web/site-compra.js new file mode 100644 index 0000000..fd2154a --- /dev/null +++ b/web/site-compra.js @@ -0,0 +1,30 @@ +/* Site DTF — the product page's buy box: grade, price per metre, billed length, + total and the add-to-cart button, beside the live sheet. It shows the item + the other site-*.js files already priced (itemAtual) and never computes a + price of its own, so the box and the cart always agree. */ +(function(){ + const vals=$('compraVals'), add=$('bAdd'); + function pintaCompra(){ + const it=itemAtual; + vals.hidden=!it; + add.disabled=!it || !itemPodeEnviar(it); + if(!it) return; + const semNota=!it.nota; + $('cNota').textContent = semNota ? 'sem nota' : it.nota; + $('cNotaBar').style.width=(semNota?0:it.nota)+'%'; + $('cNotaBar').className = semNota ? '' : it.nota>=85 ? 'ok' : it.nota>=50 ? 'av' : 'er'; + $('cTabela').textContent = TABELA[it.modo]-it.unit>0.001 ? rs(TABELA[it.modo]) : ''; + $('cMetro').textContent=rs(it.unit); + $('cMetragem').textContent=fmtM(it.cob)+' m'; + const sobraCm=Math.round((it.cob-it.metros)*100); + $('cObs').textContent = + it.cob===MINIMO_M && it.metros0 ? fmtM(it.metros)+' m de folha, arredondado a cada 10 cm.' : ''; + $('cTotal').textContent=rs(it.total); + } + add.addEventListener('click',()=>adicionaAoCarrinho()); + addEventListener('dtf-cart-changed',pintaCompra); + pintaCompra(); +})(); diff --git a/web/site-flow.js b/web/site-flow.js index 5ddc13f..3a2250f 100644 --- a/web/site-flow.js +++ b/web/site-flow.js @@ -21,7 +21,7 @@ $('qOk').addEventListener('click',()=>{ // The finished item joins the order and the product page starts over empty. function guardaItem(){ if(itemAtual && !itemPodeEnviar(itemAtual)){ - $('qual').scrollIntoView({behavior:'smooth',block:'nearest'}); + $('ciente').scrollIntoView({behavior:'smooth',block:'center'}); return false; } if(itemAtual){ pedido.push(itemAtual); itemAtual=null; } @@ -33,7 +33,7 @@ function guardaItem(){ return true; } function adicionaAoCarrinho(){ - if(!itemAtual){ $('qual').scrollIntoView({behavior:'smooth',block:'nearest'}); return; } + if(!itemAtual){ $('compra').scrollIntoView({behavior:'smooth',block:'nearest'}); return; } if(guardaItem()) vaiPara(CARRINHO); } $('pOk').addEventListener('click',adicionaAoCarrinho); diff --git a/web/site-pdf.js b/web/site-pdf.js index 5e4e915..ca9b8cb 100644 --- a/web/site-pdf.js +++ b/web/site-pdf.js @@ -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'+(a.src?'':''+ext+'')+'
'; - const Wt=(L*e+6).toFixed(1); // +6 = borda e respiro da tira - return '
'+ - '
'+ - '
'+n1(a.cm*propDe(a))+' cm
'+ - '
'+ - '
'+n1(L)+' cm de filme
'+ - '
'+pecas+ - (sobra>0.25?'
sobra '+n1(sobra)+' cm
':'')+ - '
'+ - '
'+ - '
'+ - '
'+n1(a.cm)+' cm de largura · '+n+ - (n>1?' peças':' peça')+' por fileira'+ - (sobra>0.25?' · sobram '+n1(sobra)+' cm':' · largura toda aproveitada')+'
'+ - '
'; -} 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='
'; + const fila=cabemNaLinha(a.cm); calc=''+n+'% · '+dpi+' DPI'+ - a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm'; + a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm · '+fila+' por fileira'; const sug=sugestao(a); - dica = sug - ? '
Reduzindo para '+n1(sug.alvo)+' cm você economiza '+ - n1(sug.cm)+' cm de folhaPassam 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.
' + // 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 = dpi0 && maxCmPequena para '+n1(a.cm)+' cm. Em '+DPI_AVISA+' DPI ela sai com até '+ + n1(maxCm)+' cm, ou envie um arquivo maior.' + : sug + ? '
Com '+n1(sug.alvo)+' cm cabem '+sug.para+' por fileira e a folha encolhe '+ + n1(sug.cm)+' cm.
' : ''; - dica = vizPeca(a) + dica; } return '
'+escapeHTML(a.f.name)+''+ ''+ diff --git a/web/site-steps.js b/web/site-steps.js index f91a031..bc4573c 100644 --- a/web/site-steps.js +++ b/web/site-steps.js @@ -71,11 +71,10 @@ function vaiPara(url){ const r=el.getBoundingClientRect(); return r.height>0 && r.top0; } - // Where the phone bar's button leads: the button that finishes the item on a - // product page, the order summary in the cart. + // 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 $('prev').classList.contains('on') ? $('pOk') : $('qOk'); + if(rota()==='produto' && itemAtual) return $('bAdd'); if(rota()==='carrinho' && (pedido.length || itemAtual) && !temCheckout()) return document.querySelector('.resumo'); return null; diff --git a/web/site-v2.css b/web/site-v2.css index 4edd96e..fead57e 100644 --- a/web/site-v2.css +++ b/web/site-v2.css @@ -93,28 +93,98 @@ section+section{border-top:0} .passos a.feito span{background:var(--verde-fundo);border-color:var(--verde-fundo);color:var(--verde)} .passos i{width:48px;height:2px;background:#E0E0E0} -/* Montagem: artworks on the left, the live sheet fixed on the right. */ -.voltar{height:40px;padding:0 14px;border-radius:8px;border:1px solid #D6D6D6;font-size:14px;margin-bottom:16px} +/* Product page (Montagem): artworks on the left; on the right a buy box that + stays in view with the live sheet, the grade, the price, the total and the + add-to-cart button. The old quality and preview panels are not shown: their + parts live in the buy box, and the rest repeated it (the findings list too: + each row already says what was checked). */ +#qual,#prev,#rA,.qb2,#qitens{display:none!important} +.voltar{height:auto;padding:0;border:0;background:none;box-shadow:none;font-size:14px;font-weight:500;color:var(--fraco);margin:24px 0 14px} +.voltar:hover{color:var(--laranja-escuro)} .focoGrid{grid-template-columns:minmax(0,1fr);gap:20px} -.foco2{grid-template-columns:minmax(0,1fr) 440px;gap:28px} -.mont{top:var(--topo-passos,128px)} -.req{position:static;padding:22px 24px} -.req .reqcab br{display:none} -@media(max-width:1100px){.foco2{grid-template-columns:1fr}.mont{position:static}} -.cat,.cat.file{border:1px solid var(--linha);border-width:1px;border-radius:16px} -.cabfoco,.cat.file .cabfoco{background:#fff} -.cabfoco h3{font-size:26px;font-weight:800;letter-spacing:-.02em} -.cabfoco .pr2{font-size:15px;font-weight:700;color:var(--texto2)} -.zona{border:2px dashed #F2B25A;background:var(--aviso-fundo);border-radius:var(--raio)} -.zona .ico{background:var(--laranja);color:var(--preto)} -.zona:hover,.zona.drag{border-color:var(--laranja2)} +.cat,.cat.file{border:0;background:transparent;box-shadow:none;overflow:visible} +.cabfoco,.cat.file .cabfoco{padding:0 0 20px!important;background:transparent;border:0;display:grid; + grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"t e" "s e" "p e";column-gap:24px;align-items:end} +.cabfoco>div:first-child{grid-area:t;display:flex;flex-direction:column-reverse;gap:4px} +.cabfoco .lg{font-size:12px;color:#707070;letter-spacing:.05em} +.cabfoco h3{font-size:32px;font-weight:800;letter-spacing:-.02em;line-height:1.1} +.cabfoco p{grid-area:s;margin-top:6px;font-size:15px;color:var(--texto2)} +.cabfoco .pr2{grid-area:p;margin-top:4px;font-size:14px;font-weight:600;color:var(--fraco);white-space:normal} +.tipoEnvio{grid-area:e;margin:0} +.tipoEnvio:not([hidden]){display:inline-flex;gap:4px;padding:4px;border-radius:10px;background:#EFEFEF} +.tipoEnvio .cam,.tipoEnvio .cam.on{border:0;box-shadow:none;background:transparent;padding:0 14px;min-height:40px;border-radius:8px;align-items:center} +.tipoEnvio .cam.on{background:#fff;box-shadow:0 1px 2px rgba(3,6,11,.14)} +.tipoEnvio .cam .mk2,.tipoEnvio .cam span,.tipoEnvio .cam em{display:none} +.tipoEnvio .cam b{margin:0;font-size:13.5px;font-weight:600;white-space:nowrap} +.tipoEnvio .cam.on b{font-weight:800} +.tipoEnvio .cam:disabled{cursor:default} +.tipoEnvio .cam:disabled:not(.on){opacity:.5} +.cat .in{padding:0!important} +.foco2{grid-template-columns:minmax(0,1fr) 420px;gap:32px} +.fila{display:flex;flex-direction:column;gap:12px} +.fila>*{margin:0} .cam{border-radius:var(--raio2)} .cam.on{border-color:var(--laranja);background:var(--aviso-fundo)} #larguraFolhaPronta{display:inline} -.mont{border-radius:var(--raio)} -.req{border-radius:var(--raio)} -.qual,.prev{border-radius:16px;border-color:var(--linha)} -.qb.chama,.pb.p{background:var(--laranja);color:var(--preto);border-color:var(--laranja);font-family:"Inter",sans-serif;font-weight:800;border-radius:var(--raio2)} +.zona{display:grid;grid-template-columns:44px minmax(0,1fr);column-gap:16px;row-gap:2px;align-items:center;text-align:left;padding:16px 20px;margin:0;border:2px dashed #F2B25A;background:var(--aviso-fundo);border-radius:var(--raio)} +.zona .ico{grid-row:1/4;width:44px;height:44px;line-height:44px;margin:0;border-radius:10px;background:var(--laranja);color:var(--preto);box-shadow:none} +.zona b{font-size:15px;margin:0} +.zona span{max-width:none;margin:0;font-size:13px;color:var(--fraco)} +.zona .bt{display:none} +.zona:hover,.zona.sobre{border-color:var(--laranja2);box-shadow:none} +#lista{display:flex;flex-direction:column;gap:10px} +#lista:empty{display:none} +.art{margin:0;padding:14px 16px;border-radius:12px;background:#fff;border:1px solid var(--linha)} +.art.nova{background:#fff;box-shadow:0 0 0 2px rgba(255,168,26,.45)} +.art .l1 b{font-size:15px;font-weight:700} +.art label{font-size:12px} +.art input{height:42px;font-size:15px;font-weight:600;border-radius:8px} +.art .cps{grid-template-columns:repeat(2,minmax(0,160px))} +.rep{margin:0;padding:14px 16px;border-radius:12px;background:#fff;border:1px solid var(--linha)} +.calc{font-size:13px;margin-top:8px} +.dica{display:flex;align-items:center;gap:12px;margin-top:10px;padding:10px 12px;border:0;border-radius:8px;background:var(--verde-fundo);font-size:13px;color:#14532D} +.dica.av{background:var(--aviso-fundo);color:#5A3A00} +.dica button{display:inline-flex;width:auto;margin:0 0 0 auto;flex:none;height:34px;align-items:center;padding:0 12px;border-radius:7px;border:1px solid var(--verde);background:#fff;color:var(--verde);box-shadow:none;font-size:13px} +.dica button:hover{background:var(--verde);color:#fff;box-shadow:none} +.req{position:static;padding:16px 20px 6px;border-radius:12px;box-shadow:none} +.reqcab{font-size:15px;margin-bottom:6px} + +/* The buy box */ +.mont{position:sticky;top:var(--topo-passos,128px);padding:20px;border-radius:16px;background:#fff;box-shadow:none;display:flex;flex-direction:column;gap:12px} +.mont>*{margin:0} +.montcab b{font-size:17px;font-weight:800} +.montpe{font-size:12px;text-align:center} +.compra{display:flex;flex-direction:column;gap:12px;padding-top:16px;border-top:1px solid #EFEFEF} +.compra .aoVivo{margin:0} +.compra .aoVivo.pronto{display:none} +#compraVals{display:flex;flex-direction:column;gap:10px;font-size:15px} +#compraVals[hidden]{display:none} +.cNota{display:flex;align-items:center;gap:12px} +.cNota span,.cLin>span:first-child{color:var(--texto2)} +.cNota>i{flex:1;height:6px;border-radius:999px;background:#EFEFEF;overflow:hidden} +.cNota>i i{display:block;height:100%;border-radius:999px;background:#C9CCD1} +.cNota>i i.ok{background:var(--verde)}.cNota>i i.av{background:var(--laranja)}.cNota>i i.er{background:var(--vermelho)} +.cNota b{font-family:"Inter",sans-serif;font-weight:800} +.cLin{display:flex;align-items:baseline;gap:8px} +.cLin>span:last-child,.cLin>b{margin-left:auto;display:flex;gap:8px;align-items:baseline} +.cLin s{font-size:13px;color:#707070} +.cObs{margin:-4px 0 0;font-size:13px;color:var(--verde)} +.cObs:empty{display:none} +.cTot{display:flex;align-items:baseline;padding-top:12px;border-top:1px solid #EFEFEF} +.cTot span{font-family:"Inter",sans-serif;font-weight:700} +.cTot b{margin-left:auto;font-family:"Inter",sans-serif;font-size:30px;font-weight:900;letter-spacing:-.01em} +#qmsg{font-size:13px;line-height:1.45} +.compra .ciente{margin:0;padding:12px 14px} +.compra .ciente h4{font-size:13.5px} +.compra .ciente p,.compra .ciente .lista{font-size:13px} +.cAdd{height:54px;border-radius:var(--raio2);border:0;background:var(--laranja);color:var(--preto);font-family:"Inter",sans-serif;font-weight:800;font-size:16px;cursor:pointer} +.cAdd:hover:not(:disabled){background:#FFB53D} +.cAdd:disabled{background:#EDEDED;color:#8A8D92;cursor:not-allowed} +.cPe{margin:0;font-size:13px;color:var(--fraco);text-align:center} +@media(max-width:1100px){.foco2{grid-template-columns:1fr}.mont{position:static}} +@media(max-width:620px){.montcab b{font-size:15px} + .cabfoco,.cat.file .cabfoco{grid-template-columns:1fr;grid-template-areas:"t" "s" "p" "e"} + .tipoEnvio:not([hidden]){display:grid;grid-template-columns:1fr 1fr;margin-top:14px}} /* The quality, preview, cart and checkout panels sit directly in #envio, outside .w; they share the page column so nothing runs edge to edge. */