feat: skeletons where the page waits, no flicker while a sheet is read, repeated copies in the preview
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 2m30s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 57s
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 2m30s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 57s
Reading a large sheet repainted the list and "Sua folha" on every progress step, so the preview flickered; progress now moves the bar in place and "Sua folha" shows a skeleton until the preview is ready. A sheet printed N times is shown N times (up to six, then a count). The payment page shows skeletons while the order loads and until the card form is ready. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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'); }
|
||||
|
||||
@@ -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;k<Math.min(vezes,REPETE_MAX);k++){
|
||||
const imagem=new Image(); imagem.src=x.previewSrc; imagem.alt=k? '' : x.f.name;
|
||||
imagem.style.maxHeight=Math.floor(520/Math.min(vezes,REPETE_MAX))+'px'; pilha.append(imagem);
|
||||
}
|
||||
if(vezes>REPETE_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{
|
||||
|
||||
@@ -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
|
||||
? '<div class="prog"><i style="width:'+x.pct+'%"></i></div>'+
|
||||
'<span class="progT">'+(x.pct<50?'lendo o arquivo…':
|
||||
x.pct<70?'medindo a folha…':'conferindo a arte…')+'</span>'
|
||||
'<span class="progT">'+textoProgresso(x)+'</span>'
|
||||
: x.semAnalise
|
||||
? '<div class="progF"><b>Não conseguimos conferir esta folha.</b> '+escapeHTML(x.semAnalise)+
|
||||
'. Seguimos com o preço de tabela — se quiser a nota e o desconto, exporte a mesma '+
|
||||
'folha em <b>PNG</b>, que a conferência é bem mais leve.</div>'
|
||||
: '';
|
||||
return '<div class="rep"><div class="l1"><b>'+
|
||||
return '<div class="rep" data-folha="'+i+'"><div class="l1"><b>'+
|
||||
(folhas.length>1? (i+1)+'. ':'')+escapeHTML(x.f.name)+'</b>'+
|
||||
'<span style="color:var(--fraco);font-size:10.5px">'+fmt(x.f.size)+'</span>'+
|
||||
'<button data-rmf="'+i+'">×</button></div>'+
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user