diff --git a/tests/browser_test.mjs b/tests/browser_test.mjs index 2f2bdda..2751901 100644 --- a/tests/browser_test.mjs +++ b/tests/browser_test.mjs @@ -100,6 +100,12 @@ try{ await site.fill('#fZap','11999999999'); await site.fill('#fMail','local-browser@example.test'); assert.equal(await site.eval('pedido[0].localFiles[0].name'),'local-test.cdr'); + // An unchecked item stays in the cart but is not paid for now. + await site.click('[data-sel="0"]'); + assert.deepEqual(await site.eval(`({marcado:pedido[0].marcado,pagar:itensAPagar().length,zero:$('carrTot').textContent.includes('0,00'),row:document.querySelector('.item').classList.contains('fora')})`), + {marcado:false,pagar:0,zero:true,row:true}); + await site.click('[data-sel="0"]'); + assert.equal(await site.eval('pedido[0].marcado===true && itensAPagar().length===1'),true); assert.equal(await site.eval('pedido[0].total'),21.89); assert.equal(await site.eval('document.getElementById("bPagar").disabled'),false); await site.click('#bPagar'); diff --git a/web/cart.js b/web/cart.js index 430b0c2..e826179 100644 --- a/web/cart.js +++ b/web/cart.js @@ -18,7 +18,7 @@ try{ if(!items.length){await transaction('readwrite',store=>store.delete(scope));return;} await transaction('readwrite',store=>store.put({items,expires:Date.now()+86400000},scope)); - notice.textContent='Carrinho salvo neste navegador por 24 horas. Você pode remover itens e adicionar outros após recarregar.'; + notice.textContent=''; }catch(error){notice.textContent='Não foi possível salvar o carrinho neste navegador. Mantenha esta página aberta até enviar o pedido.';} } window.dtfClearCart=async()=>{clearTimeout(timer);if(database&&scope)await transaction('readwrite',store=>store.delete(scope));notice.textContent='';}; diff --git a/web/checkout.js b/web/checkout.js index 5382f6d..fd7940c 100644 --- a/web/checkout.js +++ b/web/checkout.js @@ -15,7 +15,7 @@ let quotedCart = localStorage.getItem('dtf-quote-cart'); let refreshVersion = 0; function cartSnapshot() { - const items=[...pedido,...(itemAtual?[itemAtual]:[])]; + const items=itensAPagar(); return JSON.stringify({customer:cliente,delivery:entrega,items:items.map(item=>({ mode:item.modo,metres:item.metros,grade:item.nota,production:item.production, quality:item.qualityStatus, @@ -127,7 +127,7 @@ arquivos.forEach(f=>enviar(f).catch(()=>{})); pintaEnvio(); } - const arquivosDoCarrinho=()=>[...pedido,...(busy&&itemAtual?[itemAtual]:[])].flatMap(it=>it.localFiles||[]); + const arquivosDoCarrinho=()=>[...pedido.filter(it=>it.marcado!==false),...(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'; // Where the uploads stand, and roughly how long is left from the recent speed. @@ -208,7 +208,7 @@ // The cart's package: billed metres and value. The charged freight is // quoted again by the server from the approved items. const pacote = () => { - const items=[...pedido,...(itemAtual?[itemAtual]:[])]; + const items=itensAPagar(); return {metres:items.reduce((t,it)=>t+(it.cob||0),0), cents:Math.round(items.reduce((t,it)=>t+(it.total||0),0)*100)}; }; let freightQuoted = null; @@ -247,7 +247,7 @@ if (!clienteOk() || !entrega.cotado) return; if (!enderecoOk()) return message('Preencha o endereço de entrega.'); if (!cartPodeEnviar()) return message('Revise a qualidade e confirme a ressalva de cada item antes de enviar o pedido.'); - const cart = [...pedido,...(itemAtual?[itemAtual]:[])]; + const cart = itensAPagar(); if (!cart.length) return message('Adicione um item ao pedido.'); const initialCart=cartSnapshot(); busy = true; @@ -333,8 +333,7 @@ event.target.disabled=true; try { const order=await api('/orders/dev-paid',{quote_id:draftId}); - pedido=[]; itemAtual=null; limpaPaineis(); - await window.dtfClearCart?.(); + await pagos(); message('Pedido #'+order.number+' criado e disponível no Kanban.'); await refresh(); } catch(error) { message(error.message); event.target.disabled=false; } @@ -360,8 +359,7 @@ const quote=await api('/quotes/'+draftId); if (quote.status==='paid') { clearInterval(waitTimer); - pedido=[]; itemAtual=null; limpaPaineis(); - await window.dtfClearCart?.(); + await pagos(); await refresh(); } else if (quote.payment?.status==='rejected') { // Refused after the bank's confirmation or the provider's review. @@ -397,6 +395,11 @@ actions.append(box); } const cartOk=()=>!!quotedCart && quotedCart===cartSnapshot(); + // What was paid leaves the cart; unchecked items stay for another order. + async function pagos() { + pedido=pedido.filter(it=>it.marcado===false); itemAtual=null; limpaPaineis(); + if (pedido.length) pintaPedido(); else await window.dtfClearCart?.(); + } // Paying: card (the default, paid on this page) or PIX (paid on its own page). async function escolhaPagamento(quote, version) { diff --git a/web/index.html b/web/index.html index 78a29fe..44aeb23 100644 --- a/web/index.html +++ b/web/index.html @@ -1094,8 +1094,6 @@ footer a:hover{color:var(--laranja2)}

Seus dados

-

A conta nasce aqui, no pagamento. O CNPJ é o mesmo da nota fiscal, - e é por ele que suas artes ficam guardadas para o próximo pedido.

diff --git a/web/site-cart.js b/web/site-cart.js index 5e76dbc..38714ab 100644 --- a/web/site-cart.js +++ b/web/site-cart.js @@ -15,8 +15,13 @@ function itemPodeEnviar(item){ return item.qualityStatus==='ok' || item.qualityStatus==='unverified' || (item.qualityStatus==='warning' && item.qualityAcknowledged===true); } +// Os itens marcados no carrinho são os que vão para o pagamento; um item +// desmarcado fica guardado para depois. O item aberto no produto sempre vai. +function itensAPagar(){ + return [...pedido.filter(it=>it.marcado!==false),...(itemAtual?[itemAtual]:[])]; +} function cartPodeEnviar(){ - const items=[...pedido,...(itemAtual?[itemAtual]:[])]; + const items=itensAPagar(); return items.length>0 && items.every(itemPodeEnviar); } function limpaPaineis(){ @@ -28,9 +33,17 @@ function limpaPaineis(){ // O carrinho junta itens de produtos diferentes e cobra uma vez só. function pintaPedido(){ $('itens').innerHTML = pedido.map((it,i)=> - '
'+escapeHTML(it.tit)+''+escapeHTML(it.desc)+'
'+ + '
'+ + ''+ + ''+(it.mini ? '' : ''+escapeHTML(it.ext||'')+'')+ + (it.arquivos>1 ? '+'+(it.arquivos-1)+'' : '')+''+ + '
'+escapeHTML(it.tit)+''+escapeHTML(it.desc)+'
'+ '
'+rs(it.total)+'
'+ '
').join(''); + $('itens').querySelectorAll('[data-sel]').forEach(c=>c.addEventListener('change',()=>{ + pedido[+c.dataset.sel].marcado=c.checked; pintaPedido(); + })); $('itens').querySelectorAll('[data-rmi]').forEach(b=>b.addEventListener('click',()=>{ const i=+b.dataset.rmi, [item]=pedido.splice(i,1); pintaPedido(); @@ -39,11 +52,12 @@ function pintaPedido(){ }); })); $('bEsvaziar').hidden = pedido.length+(itemAtual?1:0) < 2; - const itens = pedido.reduce((t,it)=>t+it.total,0) + (itemAtual? itemAtual.total : 0); + const aPagar = itensAPagar(); + const itens = aPagar.reduce((t,it)=>t+it.total,0); const frete = entrega.tipo==='frete' && entrega.cotado ? entrega.valor : 0; const soma = itens + frete; - const n = pedido.length + (itemAtual?1:0); - const eco = [...pedido, ...(itemAtual?[itemAtual]:[])] + const n = aPagar.length, noCarrinho = pedido.length + (itemAtual?1:0); + const eco = aPagar .reduce((t,it)=>t+(TABELA[it.modo]-it.unit)*it.cob, 0); $('resLin').innerHTML = '
'+n+(n===1?' item':' itens')+''+rs(itens+eco)+'
'+ @@ -56,13 +70,13 @@ function pintaPedido(){ '
Revisão e reencaixe'+ 'Grátis
'; $('carrTot').textContent=rs(soma); - $('bPagarTx').textContent = (pedido.length + (itemAtual?1:0))>1 - ? 'Pagar os '+(pedido.length+(itemAtual?1:0))+' itens de uma vez' + $('bPagarTx').textContent = n>1 + ? 'Pagar os '+n+' itens de uma vez' : 'Ir para o pagamento'; $('bPagar').disabled = !cartPodeEnviar() || !entrega.cotado || !clienteOk() || !enderecoOk(); if(pedido.length || itemAtual) $('carr').classList.add('on'); - if($('cartN')){ $('cartN').textContent=n>99?'99+':n; $('cartN').hidden=!n; - $('cartLink').setAttribute('aria-label','Carrinho, '+n+(n===1?' item':' itens')); } + if($('cartN')){ $('cartN').textContent=noCarrinho>99?'99+':noCarrinho; $('cartN').hidden=!noCarrinho; + $('cartLink').setAttribute('aria-label','Carrinho, '+noCarrinho+(noCarrinho===1?' item':' itens')); } window.dispatchEvent(new Event('dtf-cart-changed')); } @@ -112,6 +126,9 @@ function carrinho(){ } itemAtual={modo, tit:MODOS[modo].tit, nota, metros, cob, unit, total:tot, localFiles:(ehFolha()?folhas:artes).map(x=>x.f), + mini:(ehFolha()?folhas:artes).map(x=>x.miniSrc).find(Boolean)||null, + ext:extDe(((ehFolha()?folhas:artes)[0]||{f:{name:''}}).f.name), + arquivos:(ehFolha()?folhas:artes).length, production:{version:2,film_width_cm:larguraFilme(),sources,...layout}, qualityStatus:$('ciente').classList.contains('recusa')?'rejected': $('ciente').classList.contains('on')?'warning':nota===0?'unverified':'ok', diff --git a/web/site-v2.css b/web/site-v2.css index 6086a33..703157c 100644 --- a/web/site-v2.css +++ b/web/site-v2.css @@ -302,6 +302,13 @@ section+section{border-top:0} .esvaziar:hover{color:#B8391C} .esvaziar[hidden]{display:none} .item{align-items:center} +.item .marca{flex:none;display:flex;align-items:center;cursor:pointer} +.item .marca input{width:20px;height:20px;margin:0;accent-color:var(--laranja);cursor:pointer} +.miniItem{position:relative;flex:none;width:52px;height:52px;border-radius:8px;border:1px solid var(--linha);background:repeating-conic-gradient(#F1F1F1 0 25%,#fff 0 50%) 0 0/12px 12px;display:flex;align-items:center;justify-content:center;overflow:hidden} +.miniItem img{max-width:100%;max-height:100%;object-fit:contain} +.miniItem i{font-style:normal;font-size:12px;font-weight:700;color:var(--fraco)} +.miniItem em{position:absolute;right:2px;bottom:2px;padding:0 4px;border-radius:5px;background:rgba(3,6,11,.75);color:#fff;font-style:normal;font-size:11px;font-weight:700} +.item.fora .miniItem,.item.fora .nm2,.item.fora .vl2{opacity:.45} .item .x{flex:none;height:36px;padding:0 12px;border-radius:8px;border:1px solid #D6D6D6;background:#fff;color:var(--texto2);font-size:15px;font-weight:600;font-family:inherit;cursor:pointer} .item .x:hover{border-color:#B8391C;color:#B8391C} .desfazer{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:80;display:flex;align-items:center;gap:16px;padding:12px 14px 12px 18px;border-radius:12px;background:var(--preto);color:#fff;font-size:16px;box-shadow:0 10px 30px rgba(3,6,11,.25);max-width:calc(100% - 32px);box-sizing:border-box}