From b6a90411f1aca74d2b34938b1f9c9dc2467724ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Thu, 24 Sep 2026 18:18:10 -0300 Subject: [PATCH] feat: let customers remove items and empty the cart, with undo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each cart item has a visible "Remover" button instead of a faint ×, and carts with two or more items get "Esvaziar carrinho". Both show a "Desfazer" notice for 8 seconds, so a wrong click costs nothing. The browser test covers remove and undo. Co-Authored-By: Claude Opus 5.5 --- tests/browser_test.mjs | 5 +++++ web/index.html | 8 ++++++-- web/site-cart.js | 24 ++++++++++++++++++++++-- web/site-v2.css | 12 ++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/tests/browser_test.mjs b/tests/browser_test.mjs index 1024ba6..03bd02d 100644 --- a/tests/browser_test.mjs +++ b/tests/browser_test.mjs @@ -85,6 +85,11 @@ try{ assert.equal(await site.eval('modo'),'file'); await site.click('#cartLink'); await waitFor(()=>site.eval(`location.pathname==='/carrinho' && getComputedStyle($('carr')).display!=='none' && getComputedStyle($('foco')).display==='none'`),'cart page'); + // Removing an item is one click and can be undone. + await site.click('[data-rmi="0"]'); + assert.deepEqual(await site.eval(`({items:pedido.length,empty:!$('carrVazio').hidden||document.documentElement.hasAttribute('data-sem-itens'),undo:!$('desfazer').hidden})`),{items:0,empty:true,undo:true}); + await site.click('#desfazerBtn'); + assert.equal(await site.eval('pedido.length===1 && pedido[0].total===21.89 && $("desfazer").hidden'),true); assert.equal(await site.eval('pedido[0].localFiles[0].name'),'local-test.cdr'); assert.equal(await site.eval('pedido[0].total'),21.89); assert.equal(await site.eval('document.getElementById("bPagar").disabled'),false); diff --git a/web/index.html b/web/index.html index 86e9944..83c12f4 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}} - + @@ -1121,7 +1121,8 @@ footer a:hover{color:var(--laranja2)}

Seu pedido

-

Itens

+

Itens

+
Item que você está montando
@@ -1216,6 +1217,9 @@ footer a:hover{color:var(--laranja2)}

+ ').join(''); $('itens').querySelectorAll('[data-rmi]').forEach(b=>b.addEventListener('click',()=>{ - pedido.splice(+b.dataset.rmi,1); pintaPedido(); + const i=+b.dataset.rmi, [item]=pedido.splice(i,1); + pintaPedido(); + avisaDesfazer(item.tit+' saiu do carrinho.',()=>{ + pedido.splice(Math.min(i,pedido.length),0,item); pintaPedido(); + }); })); + $('bEsvaziar').hidden = pedido.length+(itemAtual?1:0) < 2; const itens = pedido.reduce((t,it)=>t+it.total,0) + (itemAtual? itemAtual.total : 0); const frete = entrega.tipo==='frete' && entrega.cotado ? entrega.valor : 0; const soma = itens + frete; @@ -118,4 +123,19 @@ function carrinho(){ 'o metro cai para '+rs(piso)+' — são '+rs(ganho)+' a menos neste pedido.','var(--verde)'); } } +// Removing is one click, and a wrong click is undone from the notice that follows. +let desfazerAcao=null, desfazerTimer=null; +function avisaDesfazer(texto,acao){ + desfazerAcao=acao; clearTimeout(desfazerTimer); + $('desfazerTxt').textContent=texto; $('desfazer').hidden=false; + desfazerTimer=setTimeout(fechaDesfazer,8000); +} +function fechaDesfazer(){ clearTimeout(desfazerTimer); desfazerAcao=null; $('desfazer').hidden=true; } +addEventListener('dtf-page-changed',fechaDesfazer); +$('desfazerBtn').addEventListener('click',()=>{ const acao=desfazerAcao; fechaDesfazer(); if(acao) acao(); }); +$('bEsvaziar').addEventListener('click',()=>{ + const antes=pedido.slice(); + pedido=[]; invalidaItemAtual(); pintaPedido(); + avisaDesfazer('Carrinho esvaziado.',()=>{ pedido=antes; pintaPedido(); }); +}); function qm(t,c){ const m=$('qmsg'); m.style.display='block'; m.style.color=c||'var(--verde)'; m.innerHTML=t; } diff --git a/web/site-v2.css b/web/site-v2.css index fead57e..d48bf14 100644 --- a/web/site-v2.css +++ b/web/site-v2.css @@ -192,6 +192,18 @@ section+section{border-top:0} @media(max-width:820px){#envio>.qual,#envio>.prev,#envio>.carr,#envio>.checkout{width:calc(100% - 32px)}} /* Cart */ +.itensCab{display:flex;align-items:baseline;gap:12px;margin-bottom:11px} +.itensCab h4{margin:0} +.esvaziar{margin-left:auto;border:0;background:none;padding:6px 0;font-size:13.5px;font-weight:600;color:var(--fraco);cursor:pointer;text-decoration:underline;text-underline-offset:3px} +.esvaziar:hover{color:#B8391C} +.esvaziar[hidden]{display:none} +.item{align-items:center} +.item .x{flex:none;height:36px;padding:0 12px;border-radius:8px;border:1px solid #D6D6D6;background:#fff;color:var(--texto2);font-size:13px;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:14px;box-shadow:0 10px 30px rgba(3,6,11,.25);max-width:calc(100% - 32px);box-sizing:border-box} +.desfazer[hidden]{display:none} +.desfazer button{flex:none;height:36px;padding:0 14px;border-radius:8px;border:0;background:var(--laranja);color:var(--preto);font-family:"Inter",sans-serif;font-weight:800;font-size:14px;cursor:pointer} +@media(max-width:820px){.desfazer{bottom:100px}} .carrAviso{grid-column:1/-1;font-size:13px;color:var(--fraco)} .carrAviso:empty{display:none} .carr{border-radius:16px}