From 70a76feb23529de2d1f6b2a504826fa6ef299eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Wed, 30 Sep 2026 13:13:45 -0300 Subject: [PATCH] feat: icons on the buttons, delivery, payment, status and Kanban - Buttons: add to cart (bag with plus), go to payment (lock), remove from the cart (bin). - Delivery: a pin for pickup in Franca, a truck for home delivery. - Payment: card and QR code on the methods, copy on the PIX code, a clock on its countdown, a check on the confirmation. - Status: a check when the files are sent, check, alert, redo and cross in the quality list instead of Unicode symbols, and a check on finished steps. - Kanban: a truck or pin for where an order goes, a speech bubble on the WhatsApp link, a download arrow on the file buttons. All Lucide shapes drawn inline, as in the header. Co-Authored-By: Claude Opus 5.5 --- web/checkout.js | 18 +++++++++++++----- web/index.html | 10 +++++----- web/kanban.css | 5 +++++ web/kanban.js | 21 +++++++++++++++------ web/site-cart.js | 4 ++-- web/site-config.js | 13 +++++++++++++ web/site-modes.js | 2 +- web/site-quality.js | 2 +- web/site-steps.js | 2 ++ web/site-v2.css | 16 ++++++++++++++++ 10 files changed, 73 insertions(+), 20 deletions(-) diff --git a/web/checkout.js b/web/checkout.js index ae45ad2..5382f6d 100644 --- a/web/checkout.js +++ b/web/checkout.js @@ -150,7 +150,7 @@ 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==='pronto') return 'Arquivos enviados'; if (e.fase==='verificando') return 'Arquivos enviados · verificando a segurança…'; // Until the speed is measured, the range from the file size. return 'Enviando seus arquivos: '+e.pct+'% · '+mb(e.sent)+' de '+mb(e.total)+' · '+ @@ -181,6 +181,7 @@ const i=document.createElement('i'); i.style.width=(e.fase==='verificando'?100:e.pct)+'%'; barra.append(i); el.append(barra); } + if (e?.fase==='pronto') el.insertAdjacentHTML('beforeend', icone('okCirculo')); if (texto) el.append(document.createTextNode(texto)); el.classList.toggle('ok', e?.fase==='pronto'); } @@ -188,7 +189,7 @@ 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; + $('bPagarTx').textContent = e && e.fase!=='pronto' ? (e.fase==='verificando' ? 'Verificando os arquivos…' : 'Enviando arquivos… '+e.pct+'%') : $('bPagarTx').textContent; } } // Leaving the page pauses an upload; it resumes, but the customer is warned. @@ -388,7 +389,9 @@ function confirmado(quote) { clearInterval(waitTimer); const box=node('div',null,'confirmado'); - box.append(node('h4','Pagamento confirmado'), + const titulo=node('h4','Pagamento confirmado'); + titulo.insertAdjacentHTML('afterbegin', icone('okCirculo')); + box.append(titulo, node('p','Pedido #'+quote.order.number+' recebido e enviado para a produção. Você acompanha cada etapa em Meus pedidos.')); button('Ver meus pedidos',()=>location.assign('/portal.html?order='+quote.order.id),box); actions.append(box); @@ -406,6 +409,7 @@ const radios=opcoes.map(([key,label])=>{ const el=button(label,()=>{ if(escolhido!==key){ escolhido=key; pinta(); } },grupo,'metodo'); el.setAttribute('role','radio'); el.dataset.metodo=key; + el.insertAdjacentHTML('afterbegin', icone(key==='pix'?'qrcode':'cartao')); return el; }); actions.append(node('h4','Forma de pagamento'),grupo,area); @@ -453,10 +457,14 @@ code.readOnly=true;code.value=intent.pix_qr_code;code.setAttribute('aria-label','Código PIX copia e cola'); linha.append(code); const copiar=button('Copiar código',async()=>{ - try{ await navigator.clipboard.writeText(intent.pix_qr_code); copiar.textContent='Código copiado'; } + try{ await navigator.clipboard.writeText(intent.pix_qr_code); + copiar.replaceChildren(); copiar.insertAdjacentHTML('beforeend', icone('ok')+'Código copiado'); } catch(_){ code.select(); } },linha); + copiar.insertAdjacentHTML('afterbegin', icone('copiar')); const prazo=node('p',null,'prazo'); + prazo.insertAdjacentHTML('afterbegin', icone('relogio')); + const prazoTx=node('span'); prazo.append(prazoTx); box.append(linha,prazo,node('p','Aguardando a confirmação do pagamento…','aguarda')); actions.append(box); waitForOrder(); @@ -465,7 +473,7 @@ if (!Number.isFinite(fim)) { prazo.remove(); return; } const tick=()=>{ const s=Math.max(0,Math.ceil((fim-Date.now())/1000)); - prazo.textContent='Pague em '+String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0'); + prazoTx.textContent='Pague em '+String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0'); if (s>0) return; clearInterval(pixClock); const fimBox=node('div',null,'pixBox'); diff --git a/web/index.html b/web/index.html index 7750977..95d8c2a 100644 --- a/web/index.html +++ b/web/index.html @@ -1024,7 +1024,7 @@ footer a:hover{color:var(--laranja2)} Li e entendi. Quero imprimir assim mesmo. - +

Revisamos e reencaixamos antes de imprimir, sem custo.

@@ -1070,7 +1070,7 @@ footer a:hover{color:var(--laranja2)}
- +
@@ -1115,7 +1115,7 @@ footer a:hover{color:var(--laranja2)} +

diff --git a/web/kanban.css b/web/kanban.css index d7da987..b59d611 100644 --- a/web/kanban.css +++ b/web/kanban.css @@ -202,3 +202,8 @@ table.log td{padding:10px 12px;border-bottom:1px solid #1E2226;vertical-align:to .split .list{width:auto;border-right:0;border-bottom:1px solid var(--line)} .facts,.pane .fields{grid-template-columns:1fr} } + +/* Icons beside a label: the download buttons, where a card ships, the WhatsApp link. */ +.btn.with-icon{display:inline-flex;align-items:center;gap:6px} +.card .foot .where{display:inline-flex;align-items:center;gap:4px} +a.zap{display:inline-flex;align-items:center;gap:4px;vertical-align:bottom} diff --git a/web/kanban.js b/web/kanban.js index ae9ab12..c50650a 100644 --- a/web/kanban.js +++ b/web/kanban.js @@ -54,7 +54,7 @@ function contato(customer){ const d=String(customer.zap).replace(/\D/g,'').replace(/^55(?=\d{10,11}$)/,''); const shown=d.length===11?'('+d.slice(0,2)+') '+d.slice(2,7)+'-'+d.slice(7):d.length===10?'('+d.slice(0,2)+') '+d.slice(2,6)+'-'+d.slice(6):customer.zap; const a=node('a',shown,'zap');a.href=href;a.target='_blank';a.rel='noopener noreferrer'; - a.title='Abrir conversa no WhatsApp'; + a.title='Abrir conversa no WhatsApp';a.prepend(icon(ICON_CHAT)); line.append(a);return line; } function node(tag,text,className){const e=document.createElement(tag);if(text!==undefined&&text!==null)e.textContent=text;if(className)e.className=className;return e;} @@ -80,6 +80,14 @@ function icon(path){ return svg; } const ICON_OK=['M5 12l4 4 10-10'], ICON_WARN=['M12 8v5M12 17h.01','circle 12 12 9']; +// Lucide shapes (ISC licence), in the same stroke as the two above. +const ICON_TRUCK=['M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2','M15 18H9', + 'M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62l-3.48-4.35A1 1 0 0 0 17.52 8H14','circle 17 18 2','circle 7 18 2']; +const ICON_PIN=['M20 10c0 4.99-5.54 10.19-7.4 11.8a1 1 0 0 1-1.2 0C9.54 20.19 4 14.99 4 10a8 8 0 0 1 16 0','circle 12 10 3']; +const ICON_DOWNLOAD=['M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4','m7 10 5 5 5-5','M12 15V3']; +const ICON_CHAT=['M7.9 20A9 9 0 1 0 4 16.1L2 22Z']; +// A download button: the arrow, then its label. +function downloadButton(text,fn,className){const b=button(text,fn,className);b.prepend(icon(ICON_DOWNLOAD));b.classList.add('with-icon');return b;} async function api(path,body){ const r=await fetch('/api/operator'+path,{headers:{'Content-Type':'application/json'},...(body===undefined?{}:{method:'POST',body:JSON.stringify(body)})}); const d=await r.json();if(!r.ok){const error=new Error(typeof d.detail==='string'?d.detail:'Confira os campos ('+r.status+').');error.status=r.status;throw error;}return d; @@ -200,7 +208,8 @@ function card(order){ const status=node('span',p.text,p.tone);status.style.display='flex';status.style.alignItems='center';status.style.gap='5px'; if(p.tone==='ok')status.prepend(icon(ICON_OK));if(p.tone==='warn')status.prepend(icon(ICON_WARN)); const dest=order.snapshot.destination; - foot.append(status,node('span',dest?'Entrega · '+dest.state:'Retirada','where')); + const where=node('span',dest?'Entrega · '+dest.state:'Retirada','where');where.prepend(icon(dest?ICON_TRUCK:ICON_PIN)); + foot.append(status,where); c.append(top,node('div',order.snapshot.customer.mail,'cust'),tags,foot); if(['rec','tra','fil','imp'].includes(order.state)&&Date.now()-new Date(order.updated_at)>LATE_HOURS*3600000) c.append(node('div','Parado há mais de '+LATE_HOURS+' h','late')); @@ -336,8 +345,8 @@ function itemCard(order,item,index){ else body.append(file); }else body.append(node('div','Arquivo de impressão ainda não gerado','line')); const buttons=node('div',undefined,'buttons'); - if(row?.status==='ready'&&row.detail?.source!=='original')buttons.append(button('Baixar PDF',download(row.upload_id))); - item.uploads.forEach((uid,i)=>buttons.append(button(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost'))); + if(row?.status==='ready'&&row.detail?.source!=='original')buttons.append(downloadButton('Baixar PDF',download(row.upload_id))); + item.uploads.forEach((uid,i)=>buttons.append(downloadButton(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost'))); if(spec?.placements)buttons.append(button('Manifesto',()=>manifest(spec),'btn ghost')); if(['rec','tra'].includes(order.state)&&(!row||row.status==='manual'||row.status==='failed')) buttons.append(button('Gerar novamente',async()=>{await api('/orders/'+order.id+'/print-files',{});await load();},'btn ghost')); @@ -350,7 +359,7 @@ function finalsSection(order,revisions,section){ const rev=node('div',undefined,'rev'); rev.append(node('b',(file.kind==='final'?'Final':'Correção do cliente')+' · item '+(file.item_index+1)+' · '+file.name), node('span',(file.expired?'expirado':file.active?'atual':'substituído')+' · '+when(file.created_at)+' · '+file.note,'faint')); - if(!file.expired){const b=button('Baixar',download(file.upload_id),'btn ghost');b.style.alignSelf='flex-start';rev.append(b);} + if(!file.expired){const b=downloadButton('Baixar',download(file.upload_id),'btn ghost');b.style.alignSelf='flex-start';rev.append(b);} section.append(rev); } if(!['rec','tra','cor'].includes(order.state)){if(!revisions.length)section.append(node('p','Nenhum arquivo final registrado.','faint'));return;} @@ -504,7 +513,7 @@ function quoteItem(item,fields){ grid.append(asked,m,g);body.append(grid); } const buttons=node('div',undefined,'buttons'); - item.uploads.forEach((uid,i)=>buttons.append(button(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost'))); + item.uploads.forEach((uid,i)=>buttons.append(downloadButton(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost'))); if(item.production?.placements)buttons.append(button('Manifesto',()=>manifest(item.production),'btn ghost')); body.append(buttons);box.append(preview,body);return box; } diff --git a/web/site-cart.js b/web/site-cart.js index 9ac0f46..5e76dbc 100644 --- a/web/site-cart.js +++ b/web/site-cart.js @@ -30,7 +30,7 @@ function pintaPedido(){ $('itens').innerHTML = pedido.map((it,i)=> '
'+escapeHTML(it.tit)+''+escapeHTML(it.desc)+'
'+ '
'+rs(it.total)+'
'+ - '
').join(''); + '').join(''); $('itens').querySelectorAll('[data-rmi]').forEach(b=>b.addEventListener('click',()=>{ const i=+b.dataset.rmi, [item]=pedido.splice(i,1); pintaPedido(); @@ -56,7 +56,7 @@ function pintaPedido(){ '
Revisão e reencaixe'+ 'Grátis
'; $('carrTot').textContent=rs(soma); - $('bPagar').textContent = (pedido.length + (itemAtual?1:0))>1 + $('bPagarTx').textContent = (pedido.length + (itemAtual?1:0))>1 ? 'Pagar os '+(pedido.length+(itemAtual?1:0))+' itens de uma vez' : 'Ir para o pagamento'; $('bPagar').disabled = !cartPodeEnviar() || !entrega.cotado || !clienteOk() || !enderecoOk(); diff --git a/web/site-config.js b/web/site-config.js index a59c53d..a6c8ab0 100644 --- a/web/site-config.js +++ b/web/site-config.js @@ -15,6 +15,19 @@ const ICONES={ girar:'', espelhar:'', fechar:'', + adicionar:'', + cadeado:'', + lixeira:'', + local:'', + caminhao:'', + cartao:'', + qrcode:'', + copiar:'', + relogio:'', + ok:'', + okCirculo:'', + alerta:'', + refazer:'', }; const icone=n=>''; const fmt=b=>b<1048576?(b/1024).toFixed(0)+' KB' diff --git a/web/site-modes.js b/web/site-modes.js index 7eca571..f2b0d24 100644 --- a/web/site-modes.js +++ b/web/site-modes.js @@ -37,7 +37,7 @@ function pintaModo(){ $('larguraFolhaPronta').textContent=String(c.larg).replace('.',','); $('foco2').classList.remove('sofila'); // a caixa de requisitos vale nos 4 modos $('montcabTit').textContent = ehFolha(m) ? 'Sua folha' : 'Montagem ao vivo'; - $('qOk').textContent = ehFolha(m) ? 'Adicionar ao carrinho' : 'Ver a folha montada'; + $('qOk').innerHTML = ehFolha(m) ? icone('adicionar')+'Adicionar ao carrinho' : 'Ver a folha montada'; pintaTipoEnvio(); } // O par folha montada / artes separadas fica visível e com preço nos dois lados, diff --git a/web/site-quality.js b/web/site-quality.js index 3b1ba49..e9327da 100644 --- a/web/site-quality.js +++ b/web/site-quality.js @@ -117,7 +117,7 @@ function mostraNota(itens,n,titulo,sub){ $('qbi').style.width=(semNota?0:n)+'%'; $('qbi').style.background=c; $('qitens').innerHTML=itens.map(i=> '
'+ - (i[0]==='ok'?'✓':i[0]==='av'?'!':i[0]==='fix'?'↻':'×')+''+ + icone(i[0]==='ok'?'ok':i[0]==='av'?'alerta':i[0]==='fix'?'refazer':'fechar')+''+ '
'+escapeHTML(i[1])+''+escapeHTML(i[2])+'
').join(''); $('qmsg').style.display='none'; ressalvaResolucao(); diff --git a/web/site-steps.js b/web/site-steps.js index 6de86b9..396f015 100644 --- a/web/site-steps.js +++ b/web/site-steps.js @@ -97,6 +97,8 @@ function vaiPara(url){ passos.querySelectorAll('[data-passo]').forEach(a=>{ const n=Number(a.dataset.passo); a.classList.toggle('atual',n===atual); a.classList.toggle('feito',n