diff --git a/web/checkout.js b/web/checkout.js index d09fa59..011c10b 100644 --- a/web/checkout.js +++ b/web/checkout.js @@ -86,6 +86,11 @@ rows.push(linha('Total', rs(approved.total_cents/100), 'tot')); resumo.replaceChildren(node('h4','Resumo do pedido'), ...rows); } + function esqueletoPagamento() { + const linhas=['skel skelLinha','skel skelLinha','skel skelLinha curta'].map(c=>node('div',null,c)); + resumo.hidden=false; resumo.replaceChildren(node('h4','Resumo do pedido'),...linhas); + actions.replaceChildren(node('div',null,'skel skelLinha curta'),node('div',null,'skel skelBloco')); + } function semPedido() { pintaResumo(null); actions.replaceChildren(); @@ -259,6 +264,7 @@ if (!naPagina()) { if (!go || !draftId) return; } else if (!draftId) { semPedido(); return; } const version=++refreshVersion, shownId=draftId; + if (naPagina() && !actions.children.length) esqueletoPagamento(); try { await ready; const quote=await api('/quotes/'+shownId); @@ -474,9 +480,10 @@ async function showCard(approved, into, version, tipo) { const holder=node('div'); holder.id='cardPaymentBrick'; - into.append(holder); + const espera=node('div',null,'skel skelBloco'); + into.append(espera,holder); try { await loadMercadoPago(); } - catch(error) { message(error.message); return; } + catch(error) { espera.remove(); message(error.message); return; } const session=await ready; // The customer may have switched to PIX, or the page re-rendered, meanwhile. if (!holder.isConnected || version!==refreshVersion) return; @@ -493,7 +500,7 @@ visual:{hideFormTitle:true, style:{customVariables:{baseColor:'#FFA81A',buttonTextColor:'#03060B'}}}}, callbacks:{ - onReady:()=>{}, + onReady:()=>{ espera.remove(); }, onError:error=>{ console.error(error); message('Erro no formulário do cartão. Confira os dados e tente de novo.'); }, onSubmit:async data=>{ if (!cartOk()) { await refresh(); throw new Error('cart changed'); } diff --git a/web/site-packing.js b/web/site-packing.js index e4d4f35..2c0f7ed 100644 --- a/web/site-packing.js +++ b/web/site-packing.js @@ -277,16 +277,32 @@ function previaAoVivo(){ const tot=folhaTotalM(); $('vMt').textContent = tot>0 ? fmtM(cobrar(tot))+' m' : '—'; const medidas=folhas.filter(x=>x.med); - const previews=folhas.filter(x=>x.previewSrc); + // Sheets still being read show a placeholder; a sheet printed N times is + // shown N times (up to REPETE_MAX, then a count). + const REPETE_MAX=6; + const previews=folhas.filter(x=>x.previewSrc || (x.pct!=null && x.med && x.med.grande)); if(previews.length){ const painel=document.createElement('div'); painel.className='folhaPrevias'; previews.forEach((x,index)=>{ const figura=document.createElement('figure'); figura.className='folhaPrevia'; - const imagem=new Image(); imagem.src=x.previewSrc; imagem.alt=x.f.name; const legenda=document.createElement('figcaption'); + const vezes=Math.max(1,x.rep||1); legenda.textContent=(previews.length>1 ? (index+1)+'. ' : '')+x.f.name+ - (x.med ? ' · '+n1(x.med.larg)+' × '+n1(x.med.alt)+' cm' : ''); - figura.append(imagem,legenda); painel.append(figura); + (x.med ? ' · '+n1(x.med.larg)+' × '+n1(x.med.alt)+' cm' : '')+(vezes>1 ? ' · '+vezes+' cópias' : ''); + if(!x.previewSrc){ + const esqueleto=document.createElement('div'); esqueleto.className='skel folhaSkel'; + esqueleto.style.aspectRatio=x.med ? x.med.larg+' / '+Math.min(x.med.alt,x.med.larg*4) : '1 / 2'; + esqueleto.append(Object.assign(document.createElement('span'),{textContent:'Gerando a prévia da folha…'})); + figura.append(esqueleto,legenda); painel.append(figura); return; + } + const pilha=document.createElement('div'); pilha.className='folhaPilha'; + for(let k=0;kREPETE_MAX) pilha.append(Object.assign(document.createElement('div'), + {className:'folhaMais', textContent:'+ '+(vezes-REPETE_MAX)+' cópia'+(vezes-REPETE_MAX>1?'s':'')})); + figura.append(pilha,legenda); painel.append(figura); }); $('vArea').replaceChildren(painel); }else{ diff --git a/web/site-pdf.js b/web/site-pdf.js index a10b2dd..1cb060b 100644 --- a/web/site-pdf.js +++ b/web/site-pdf.js @@ -352,6 +352,18 @@ function medirFolha(file){ }); } +function textoProgresso(x){ + if(x.med && x.med.grande) return 'gerando a prévia e conferindo a arte… '+x.pct+'%'; + return x.pct<50?'lendo o arquivo…':x.pct<70?'medindo a folha…':'conferindo a arte…'; +} +// Moves one sheet's progress bar in place: repainting the whole list and the +// preview on every step made "Sua folha" flicker while a large file was read. +function pintaProgressoFolha(x){ + const el=$('lista').querySelector('.rep[data-folha="'+folhas.indexOf(x)+'"]'); + const barra=el && el.querySelector('.prog i'), texto=el && el.querySelector('.progT'); + if(!barra){ pintaFolha(); return; } + barra.style.width=x.pct+'%'; if(texto) texto.textContent=textoProgresso(x); +} function pintaFolha(){ invalidaItemAtual(); const L=larguraFilme(); @@ -415,14 +427,13 @@ function pintaFolha(){ } const barra = x.pct!=null ? '
'+ - ''+(x.pct<50?'lendo o arquivo…': - x.pct<70?'medindo a folha…':'conferindo a arte…')+'' + ''+textoProgresso(x)+'' : x.semAnalise ? '
Não conseguimos conferir esta folha. '+escapeHTML(x.semAnalise)+ '. Seguimos com o preço de tabela — se quiser a nota e o desconto, exporte a mesma '+ 'folha em PNG, que a conferência é bem mais leve.
' : ''; - return '
'+ + return '
'+ (folhas.length>1? (i+1)+'. ':'')+escapeHTML(x.f.name)+''+ ''+fmt(x.f.size)+''+ '
'+ diff --git a/web/site-upload.js b/web/site-upload.js index 1cec483..4ab2114 100644 --- a/web/site-upload.js +++ b/web/site-upload.js @@ -84,7 +84,7 @@ function sel(fs){ let mostrado=0; previaPngGrande(x.f, 600, p=>{ const pct=60+Math.floor(p*38); - if(pct-mostrado>=3){ mostrado=pct; x.pct=pct; pintaFolha(); } + if(pct-mostrado>=1){ mostrado=pct; x.pct=pct; pintaProgressoFolha(x); } }).then(cv=>{ if(cv && folhas.includes(x)){ x.previewSrc=cv.toDataURL('image/webp',0.85); diff --git a/web/site-v2.css b/web/site-v2.css index bce35fc..c685c4c 100644 --- a/web/site-v2.css +++ b/web/site-v2.css @@ -318,3 +318,16 @@ html:is([data-rota="pagamento"],[data-rota="pix"]) .pagGrid{display:grid;grid-te @media (max-width:480px){ .etapas2 .w{grid-template-columns:1fr} } + +/* Skeleton placeholders where the page really waits */ +.skel{position:relative;border-radius:8px;background:linear-gradient(90deg,#EEEFF1 25%,#F7F7F8 37%,#EEEFF1 63%);background-size:400% 100%;animation:skel 1.4s ease infinite} +@keyframes skel{0%{background-position:100% 50%}100%{background-position:0 50%}} +@media (prefers-reduced-motion:reduce){.skel{animation:none}} +.folhaSkel{display:flex;align-items:center;justify-content:center;max-height:520px;min-height:180px;width:100%} +.folhaSkel span{font-size:12.5px;color:var(--fraco);background:#fffc;padding:4px 10px;border-radius:999px} +#vArea .folhaPilha{display:flex;flex-direction:column;gap:4px;max-height:520px;overflow:auto;background:#fff} +#vArea .folhaPilha img{width:100%;height:auto;display:block} +.folhaMais{font-size:12px;color:var(--texto2);text-align:center;padding:8px} +.skelLinha{height:14px;margin:8px 0} +.skelLinha.curta{width:40%} +.skelBloco{height:160px;margin-top:8px}