feat: choose which cart items to pay, with a thumbnail of each
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 3m22s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m13s

Each cart item has a checkbox and a thumbnail of its first file (or its
extension when there is none). Only checked items are priced, quoted,
uploaded and paid; an unchecked item stays in the cart, dimmed, for a later
order, and stays there after the others are paid. The header count is the
whole cart; the summary counts what is being paid.

Also removes the "A conta nasce aqui…" and "Carrinho salvo neste
navegador…" texts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-30 13:57:16 -03:00
parent 25fa890e92
commit 01b94da36d
6 changed files with 51 additions and 20 deletions

View File

@@ -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) {