feat: show upload progress with a bar, time left and a header badge
All checks were successful
Build and deploy / Validate source (push) Successful in 9s
Build and deploy / Integration suite on a real stack (push) Successful in 2m27s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 45s
All checks were successful
Build and deploy / Validate source (push) Successful in 9s
Build and deploy / Integration suite on a real stack (push) Successful in 2m27s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 45s
Uploads of several GB ran behind one grey line in the cart. The order summary now has a progress bar with the percentage, size and an estimate of the time left from the recent speed; the header shows "enviando 45%" on every page while it runs; the payment button says what it is waiting for; and leaving the page mid-upload asks first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -116,24 +116,57 @@
|
||||
const arquivosDoCarrinho=()=>[...pedido,...(busy&&itemAtual?[itemAtual]:[])].flatMap(it=>it.localFiles||[]);
|
||||
const gb=n=>(n/1073741824).toLocaleString('pt-BR',{maximumFractionDigits:1})+' GB';
|
||||
const mb=n=>n>=1073741824 ? gb(n) : Math.round(n/1048576)+' MB';
|
||||
function textoEnvio() {
|
||||
const files=arquivosDoCarrinho(); if(!files.length) return '';
|
||||
// Where the uploads stand, and roughly how long is left from the recent speed.
|
||||
const amostras=[];
|
||||
function estadoEnvio() {
|
||||
const files=arquivosDoCarrinho(); if(!files.length) return null;
|
||||
let total=0, sent=0, pendentes=0;
|
||||
for (const f of files) {
|
||||
const e=envios.get(chaveArquivo(f));
|
||||
total+=f.size; sent+=e ? Math.min(e.sent,f.size) : 0;
|
||||
if (!e || !e.done) pendentes++;
|
||||
}
|
||||
if (!pendentes) return 'Arquivos enviados.';
|
||||
if (sent>=total) return 'Arquivos enviados · verificando a segurança…';
|
||||
return 'Enviando seus arquivos: '+Math.floor(sent/total*100)+'% ('+mb(sent)+' de '+mb(total)+')';
|
||||
const agora=Date.now();
|
||||
amostras.push([agora,sent]); while(amostras.length>2 && agora-amostras[0][0]>15000) amostras.shift();
|
||||
const [t0,s0]=amostras[0], taxa=agora>t0 ? (sent-s0)/((agora-t0)/1000) : 0;
|
||||
const fase=!pendentes ? 'pronto' : sent>=total ? 'verificando' : 'enviando';
|
||||
return {total, sent, fase, pct:Math.floor(sent/total*100),
|
||||
restante: fase==='enviando' && taxa>0 ? (total-sent)/taxa : null};
|
||||
}
|
||||
const tempo=s=>s<60 ? 'menos de 1 min' : 'cerca de '+Math.ceil(s/60)+' min';
|
||||
function textoEnvio(e) {
|
||||
if (!e) return '';
|
||||
if (e.fase==='pronto') return 'Arquivos enviados ✓';
|
||||
if (e.fase==='verificando') return 'Arquivos enviados · verificando a segurança…';
|
||||
return 'Enviando seus arquivos: '+e.pct+'% · '+mb(e.sent)+' de '+mb(e.total)+
|
||||
(e.restante!=null ? ' · '+tempo(e.restante) : '');
|
||||
}
|
||||
function pintaEnvio() {
|
||||
const e=estadoEnvio(), texto=textoEnvio(e);
|
||||
const el=document.getElementById('envioArq');
|
||||
const texto=textoEnvio();
|
||||
if (el) el.textContent=texto;
|
||||
if (busy && texto) message(texto);
|
||||
if (el) {
|
||||
el.replaceChildren();
|
||||
if (e && e.fase!=='pronto') {
|
||||
const barra=document.createElement('div'); barra.className='envioBarra';
|
||||
const i=document.createElement('i'); i.style.width=(e.fase==='verificando'?100:e.pct)+'%'; barra.append(i);
|
||||
el.append(barra);
|
||||
}
|
||||
if (texto) el.append(document.createTextNode(texto));
|
||||
el.classList.toggle('ok', e?.fase==='pronto');
|
||||
}
|
||||
// Visible on every page while it runs.
|
||||
const topo=document.getElementById('cartEnvio');
|
||||
if (topo) topo.textContent = !e || e.fase==='pronto' ? '' : e.fase==='verificando' ? '· verificando' : '· enviando '+e.pct+'%';
|
||||
if (busy && texto) {
|
||||
message(texto);
|
||||
$('bPagar').textContent = e && e.fase!=='pronto' ? (e.fase==='verificando' ? 'Verificando os arquivos…' : 'Enviando arquivos… '+e.pct+'%') : $('bPagar').textContent;
|
||||
}
|
||||
}
|
||||
// Leaving the page pauses an upload; it resumes, but the customer is warned.
|
||||
window.addEventListener('beforeunload',event=>{
|
||||
const e=estadoEnvio();
|
||||
if (e && e.fase==='enviando') { event.preventDefault(); event.returnValue=''; }
|
||||
});
|
||||
// Only what is in the cart: the item on the product page may still change.
|
||||
window.addEventListener('dtf-cart-changed',()=>{ arquivosDoCarrinho().forEach(f=>{ enviar(f).catch(()=>{}); }); pintaEnvio(); });
|
||||
async function upload(file) {
|
||||
|
||||
@@ -765,7 +765,7 @@ footer a:hover{color:var(--laranja2)}
|
||||
</div>
|
||||
<div class="acts">
|
||||
<a href="/portal.html">Minha conta</a>
|
||||
<a href="/carrinho" id="cartLink">Carrinho (0)</a>
|
||||
<a href="/carrinho" id="cartLink">Carrinho (0)</a><span class="cartEnvio" id="cartEnvio" aria-live="polite"></span>
|
||||
</div>
|
||||
</div></header>
|
||||
|
||||
|
||||
@@ -229,7 +229,12 @@ section+section{border-top:0}
|
||||
/* The payment pages: paying on the left, the order summary on the right */
|
||||
html:is([data-rota="pagamento"],[data-rota="pix"]) .checkout{max-width:1040px;margin-top:8px}
|
||||
html:is([data-rota="pagamento"],[data-rota="pix"]) .pagGrid{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:32px;align-items:start}
|
||||
.envioArq{font-size:13px;color:var(--texto2);margin-top:12px;line-height:1.5}
|
||||
.envioArq{font-size:13px;color:var(--texto2);margin-top:14px;line-height:1.5}
|
||||
.envioArq.ok{color:var(--verde)}
|
||||
.envioBarra{height:6px;border-radius:999px;background:#EDEDED;overflow:hidden;margin-bottom:6px}
|
||||
.envioBarra i{display:block;height:100%;background:var(--laranja);border-radius:999px;transition:width .4s}
|
||||
.cartEnvio{font-size:13px;font-weight:700;color:var(--laranja-escuro);margin-left:6px}
|
||||
.cartEnvio:empty{display:none}
|
||||
.envioArq:empty{display:none}
|
||||
.dicaEnd{font-size:13px;color:var(--texto2);margin-top:12px}
|
||||
.dicaEnd:empty{display:none}
|
||||
|
||||
Reference in New Issue
Block a user