/* Minha conta: sign-in, the overview, the orders (filtered, a page at a time, with the cart waiting for payment among them) and the account's details. One page; the address says which part shows (/conta, /conta/entrar, /conta/pedidos, /conta/dados). */ const $=id=>document.getElementById(id); const node=(tag,text,className)=>{const el=document.createElement(tag);if(text!==undefined&&text!==null)el.textContent=text;if(className)el.className=className;return el;}; const money=c=>(c/100).toLocaleString('pt-BR',{style:'currency',currency:'BRL'}); const quando=d=>new Date(d).toLocaleString('pt-BR',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}); const dia=d=>new Date(d).toLocaleDateString('pt-BR',{day:'2-digit',month:'2-digit',year:'2-digit'}); const metros=m=>Number(m).toLocaleString('pt-BR',{minimumFractionDigits:2,maximumFractionDigits:2})+' m'; const soDigitos=v=>String(v||'').replace(/\D/g,''); const fmtCnpj=v=>soDigitos(v).replace(/^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$/,'$1.$2.$3/$4-$5'); const fmtZap=v=>{const d=soDigitos(v);return 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):v;}; const fmtCep=v=>{const d=soDigitos(v).slice(0,8);return d.length>5?d.slice(0,5)+'-'+d.slice(5):d;}; // Lucide shapes (ISC licence), the same as on the Site. const ICONES={ baixar:'', local:'', caminhao:'', ok:'', alerta:'', }; function icone(nome){ const span=document.createElement('span'); span.innerHTML=''; return span.firstChild; } const PRODUTO={file:'DTF Têxtil · folha montada',avulsa:'DTF Têxtil · artes avulsas', uvfile:'DTF UV · folha montada',uv:'DTF UV · artes avulsas'}; const TRILHA=['rec','tra','fil','imp','fin']; const TAMANHO=10; let scope,states={},environment='',conta=null; let filtro={status:'all',number:'',days:0,page:1}; async function api(path,body){const r=await fetch('/api'+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 informados.');error.status=r.status;throw error;}return d;} const aviso=(text,erro)=>{$('message').textContent=text||'';$('message').classList.toggle('erro',!!erro);}; function button(text,fn,className='bSec'){const b=node('button',text,className);b.type='button';b.onclick=async event=>{event?.stopPropagation?.();b.disabled=true;try{await fn();}catch(e){aviso(e.message,true);}finally{b.disabled=false;}};return b;} function forgetCheckout(){for(const k of ['dtf-quote','dtf-request-key','dtf-request-body'])localStorage.removeItem(k);} // ---- Addresses ----------------------------------------------------------- function vista(){ const p=location.pathname.replace(/\/+$/,''); if(p==='/conta/entrar') return 'entrar'; if(p==='/conta/dados') return 'dados'; if(p==='/conta') return 'geral'; return 'pedidos'; // /conta/pedidos, and the old /portal.html } function ir(caminho){history.pushState({},'',caminho);mostra().catch(e=>aviso(e.message,true));} window.addEventListener('popstate',()=>mostra().catch(e=>aviso(e.message,true))); document.addEventListener('click',event=>{ const a=event.target.closest('a[data-view]'); if(!a||event.metaKey||event.ctrlKey||event.shiftKey)return; event.preventDefault();ir(a.getAttribute('href')); }); async function mostra(){ aviso(''); const v=vista(), proximo=new URLSearchParams(location.search).get('next'); // Overview and details are the account's; orders also work for a guest. if((v==='geral'||v==='dados')&&!conta){history.replaceState({},'','/conta/entrar?next='+encodeURIComponent(location.pathname));return mostra();} if(v==='entrar'&&conta){history.replaceState({},'',proximo&&/^\/conta(\/[a-z]+)?$/.test(proximo)?proximo:'/conta');return mostra();} $('auth').hidden=v!=='entrar'; $('area').hidden=v==='entrar'; $('vGeral').hidden=v!=='geral';$('vPedidos').hidden=v!=='pedidos';$('vDados').hidden=v!=='dados'; for(const a of document.querySelectorAll('.lateral a'))a.classList.toggle('on',a.dataset.view===v); for(const a of document.querySelectorAll('.lateral [data-conta]'))a.hidden=!conta; $('navEntrar').hidden=!!conta;$('logout').hidden=!conta; document.title=({entrar:'Entrar',geral:'Minha conta',pedidos:'Pedidos',dados:'Dados da conta'})[v]+' · DTF Dropstar'; if(v==='geral')await geral(); else if(v==='pedidos')await pedidos(); else if(v==='dados')dados(); } // ---- Orders -------------------------------------------------------------- function trilha(state){ const ol=node('ol',null,'trilha'); const atual=TRILHA.indexOf(state); TRILHA.forEach((s,i)=>{ const feito=it+Number(i.billed_metres||0),0); return (items.length===1?PRODUTO[items[0].mode]||items[0].mode:items.length+' itens')+(total?' · '+metros(total):''); } function linha(e){ const row=node('div',null,'linha'); const [rotulo,cls]=estado(e); const num=node('b',e.kind==='quote'?'Carrinho':'#'+e.number,'num'); const oque=node('div',null,'oque');oque.append(node('span',resumoItens(e.items)),node('span',dia(e.created_at),'data')); const st=node('span',null,'estadoW');st.append(node('span',rotulo,'estado '+cls)); if(e.kind==='quote'&&e.pay_with==='pix')st.append(node('span','PIX','via')); const valor=node('b',e.total_cents!=null?money(e.total_cents):'—','valor'); const acao=node('div',null,'acao'); row.append(num,oque,st,valor,acao); const wrap=node('div',null,'item'+(e.kind==='quote'?' pendente':'')+(e.state==='cor'?' correcao':'')); wrap.append(row); if(e.kind==='quote'){ if(e.state==='pay'){ const pagar=node('a','Pagar','bPri bMini'); pagar.href=(e.pay_with==='pix'?'/pagamento/pix':'/pagamento')+'?quote='+e.id; acao.append(pagar); } return wrap; } wrap.id='order-'+e.id; const detalhe=node('div',null,'detalhe');detalhe.hidden=true; const alterna=async()=>{ if(!detalhe.hidden){detalhe.hidden=true;ver.textContent='Ver';return;} await details(e.id,detalhe,e.created_at);detalhe.hidden=false;ver.textContent='Fechar'; }; const ver=button('Ver',alterna); acao.append(ver); row.addEventListener('click',event=>{if(!event.target.closest('button,a'))ver.click();}); wrap.append(detalhe); wrap.abrir=async()=>{if(detalhe.hidden)await alterna();}; return wrap; } function filtros(counts,groups){ const box=$('filtros');box.replaceChildren(); const todos=Object.values(counts).reduce((a,b)=>a+b,0); for(const [key,label] of [['all','Todos'],...Object.entries(groups)]){ const n=key==='all'?todos:counts[key]; const chip=node('button',label+(n?' · '+n:''),'chipF'+(filtro.status===key?' on':''));chip.type='button'; chip.setAttribute('aria-pressed',String(filtro.status===key)); chip.onclick=()=>{filtro={...filtro,status:key,page:1};pedidos().catch(x=>aviso(x.message,true));}; box.append(chip); } } function paginas(total){ const box=$('paginas');box.replaceChildren(); const n=Math.ceil(total/TAMANHO); if(n<=1){if(total)box.append(node('span',total+(total===1?' pedido':' pedidos'),'nota2'));return;} const de=(filtro.page-1)*TAMANHO+1, ate=Math.min(total,filtro.page*TAMANHO); box.append(node('span',de+'–'+ate+' de '+total+' pedidos','nota2')); const nav=node('div',null,'pags'); const vai=p=>{filtro.page=p;pedidos().then(()=>$('vPedidos').scrollIntoView({block:'start'})).catch(x=>aviso(x.message,true));}; const b=(t,p,label)=>{const x=node('button',t,'pag'+(p===filtro.page?' on':''));x.type='button';if(label)x.setAttribute('aria-label',label); if(p===filtro.page)x.setAttribute('aria-current','page');else if(p<1||p>n)x.disabled=true;else x.onclick=()=>vai(p);nav.append(x);}; b('‹',filtro.page-1,'Página anterior'); for(let p=Math.max(1,filtro.page-2);p<=Math.min(n,filtro.page+2);p++)b(String(p),p); b('›',filtro.page+1,'Próxima página'); box.append(nav); } async function pedidos(){ $('guest').textContent=conta?'':'Você está vendo os pedidos feitos neste navegador. Entre ou crie uma conta para vê-los em qualquer lugar.'; const q=new URLSearchParams({status:filtro.status,days:String(filtro.days),page:String(filtro.page),size:String(TAMANHO)}); if(filtro.number)q.set('number',filtro.number); const data=await api('/customer/orders?'+q);states=data.states; filtros(data.counts,data.groups); const lista=$('orders');lista.replaceChildren(); if(!data.items.length){ const livre=filtro.status==='all'&&!filtro.number&&!filtro.days; const vazio=node('div',null,'vazio'); vazio.append(node('p',livre?'Seus pedidos aparecem aqui.':'Nenhum pedido com esse filtro.')); if(livre){const a=node('a','Enviar uma arte','bPri');a.href='/#envio';vazio.append(a);} lista.append(vazio); } for(const e of data.items)lista.append(linha(e)); paginas(data.total); const aberto=new URLSearchParams(location.search).get('order'); if(aberto&&/^[0-9a-f-]{36}$/.test(aberto)){ let alvo=$('order-'+aberto); if(!alvo){ // Not on this page of the list: shown on its own, above it. try{const o=await api('/customer/orders/'+aberto); alvo=linha({kind:'order',id:o.id,number:o.number,state:o.state,total_cents:o.snapshot.total_cents, created_at:o.created_at,items:o.snapshot.items});lista.prepend(alvo);}catch(e){alvo=null;} } if(alvo){await alvo.abrir();alvo.scrollIntoView({block:'start'});} } } let buscaTimer; $('fNumero').addEventListener('input',e=>{e.target.value=soDigitos(e.target.value);clearTimeout(buscaTimer); buscaTimer=setTimeout(()=>{filtro={...filtro,number:e.target.value,page:1};pedidos().catch(x=>aviso(x.message,true));},350);}); $('fPeriodo').addEventListener('change',e=>{filtro={...filtro,days:Number(e.target.value),page:1};pedidos().catch(x=>aviso(x.message,true));}); function itensPedido(snapshot){ const ul=node('ul',null,'itensPed'); for(const item of snapshot.items){ const li=node('li'); li.append(node('span',PRODUTO[item.mode]||item.mode,'nome'),node('span',metros(item.billed_metres),'qt'),node('b',money(item.total_cents))); ul.append(li); } const f=snapshot.freight; if(f&&f.total_cents){const li=node('li',null,'frete');li.append(node('span','Frete','nome'),node('span',f.days?'até '+f.days+' dias úteis':'','qt'),node('b',money(f.total_cents)));ul.append(li);} return ul; } function entrega(snapshot){ const d=snapshot.destination, p=node('p',null,'entregaPed'); p.append(icone(d?'caminhao':'local'),node('span',d?'Entrega pela Jadlog · '+d.street+', '+d.number+' · '+d.city+'/'+d.state:'Retirada em Franca · Av. Major Elias Motta, 1281')); return p; } async function details(id,container,criado){ const order=await api('/customer/orders/'+id);container.replaceChildren(); if(order.state==='cor'){ const p=node('p',null,'pedAlerta');p.append(icone('alerta'),node('span','A equipe precisa de uma correção na sua arte. Veja o motivo abaixo e envie o arquivo corrigido.')); container.append(p); }else container.append(trilha(order.state)); container.append(itensPedido(order.snapshot),entrega(order.snapshot)); const hist=node('div',null,'secao');hist.append(node('h4','Andamento')); const ol=node('ol',null,'historico'); // Payment is where the order starts: the first step is not a movement. if(criado){const li=node('li');li.append(node('b',states.rec||'Arte recebida'),node('span',quando(criado)));ol.append(li);} for(const h of order.history){ const li=node('li');li.append(node('b',states[h.to_state]),node('span',quando(h.created_at))); if(h.reason)li.append(node('p',h.reason,'motivo')); ol.append(li); } hist.append(ol);container.append(hist); if(order.files.length){ const arq=node('div',null,'secao');arq.append(node('h4','Arquivos')); for(const file of order.files){ const row=node('div',null,'arquivo'); const info=node('div'); info.append(node('b',file.name),node('span',(file.kind==='final'?'Arquivo final':'Correção')+' · item '+(file.item_index+1)+' · '+ (file.expired?'expirado':!file.active?'substituído':'disponível até '+dia(file.expires_at)))); row.append(info); if(file.active&&!file.expired){ const b=button('Baixar',async()=>{const d=await api('/customer/orders/'+id+'/files/'+file.id+'/download');const link=node('a');link.href=d.url;link.download=d.name;link.referrerPolicy='no-referrer';link.click();}); b.prepend(icone('baixar'));row.append(b); } arq.append(row); } container.append(arq); } if(order.state==='cor'){ const form=node('form',null,'secao correcaoForm');form.append(node('h4','Enviar arte corrigida')); const inputs=order.snapshot.items.map((item,index)=>{ const label=node('label',(index+1)+'. '+(PRODUTO[item.mode]||item.mode)); const input=node('input');input.type='file';input.multiple=true;input.dataset.item=index; form.append(label,input);return input; }); const nl=node('label','O que foi corrigido');const note=node('textarea');note.required=true;note.maxLength=1000;note.rows=3; form.append(nl,note); const submit=node('button','Enviar correção','bPri');submit.type='submit';form.append(submit); form.onsubmit=async event=>{event.preventDefault();submit.disabled=true;try{ const files=[];for(const [index,input] of inputs.entries())for(const file of input.files)files.push({item_index:index,upload_id:await dtfUpload(file,{api,scope:scope+':correction:'+id+':'+order.version,progress:text=>aviso(text)})}); if(!files.length)throw new Error('Selecione pelo menos um arquivo corrigido.'); await api('/customer/orders/'+id+'/corrections',{version:order.version,files,note:note.value}); aviso('Correção enviada. A equipe confere e segue com o pedido.');await details(id,container,criado); }catch(error){aviso(error.message,true);}finally{submit.disabled=false;}}; container.append(form); } } // ---- Overview ------------------------------------------------------------ async function geral(){ $('account').textContent='CNPJ '+fmtCnpj(conta.cnpj)+' · '+conta.mail; const data=await api('/customer/orders?page=1&size=1');states=data.states; const box=$('metricas');box.replaceChildren(); for(const [key,label] of [['pay','Aguardando pagamento'],['prod','Em produção'],['cor','Correção'],['fin','Finalizados']]){ if(key==='cor'&&!data.counts.cor)continue; const a=node('a',null,'metrica'+(key==='cor'?' alerta':''));a.href='/conta/pedidos';a.dataset.view='pedidos'; a.addEventListener('click',()=>{filtro={status:key,number:'',days:0,page:1};}); a.append(node('span',label),node('b',String(data.counts[key]))); box.append(a); } const ultimo=$('ultimo');ultimo.replaceChildren(); if(data.items.length)ultimo.append(linha(data.items[0])); else{const v=node('div',null,'vazio');v.append(node('p','Seu primeiro pedido aparece aqui.'));const a=node('a','Enviar uma arte','bPri');a.href='/#envio';v.append(a);ultimo.append(v);} } // ---- Account details ----------------------------------------------------- const CAMPOS_END=[['dNome','recipient'],['dRua','street'],['dNum','number'],['dComp','complement'],['dBairro','district'],['dCidade','city'],['dUf','state']]; function dados(){ $('dCnpj').value=fmtCnpj(conta.cnpj);$('dZap').value=fmtZap(conta.zap);$('dMail').value=conta.mail; const end=conta.address||{}; $('dCep').value=fmtCep(end.postal_code||''); for(const [id,k] of CAMPOS_END)$(id).value=end[k]||''; $('dMailSenha').value='';$('dAtual').value='';$('dNova').value=''; } $('dZap').addEventListener('input',e=>{e.target.value=fmtZap(soDigitos(e.target.value).slice(0,11));}); $('dCep').addEventListener('input',async e=>{ e.target.value=fmtCep(e.target.value); const cep=soDigitos(e.target.value); if(cep.length!==8)return; try{const a=await api('/cep/'+cep); for(const [id,v] of [['dRua',a.street],['dBairro',a.district],['dCidade',a.city],['dUf',a.state]])if(v)$(id).value=v; (['dNome','dRua','dNum'].map($).find(el=>!el.value.trim())||$('dNum')).focus(); }catch(_){/* sem consulta: o cliente preenche */} }); $('fPerfil').onsubmit=async event=>{ event.preventDefault(); const cep=soDigitos($('dCep').value); const campos=Object.fromEntries(CAMPOS_END.map(([id,k])=>[k,$(id).value.trim()])); const algum=cep||Object.values(campos).some(Boolean); const address=algum?{...campos,state:campos.state.toUpperCase(),postal_code:cep}:null; try{ const r=await api('/account/profile',{zap:$('dZap').value,address}); conta=r.customer;aviso('Dados salvos.'); }catch(e){aviso(e.status===422?(algum?'Confira o WhatsApp e o endereço: todos os campos, menos o complemento, são necessários.':'Confira o WhatsApp.'):e.message,true);} }; $('fEmail').onsubmit=async event=>{ event.preventDefault(); try{const r=await api('/account/email',{email:$('dMail').value,password:$('dMailSenha').value}); conta=r.customer;$('dMailSenha').value='';$('quem').textContent=conta.mail;aviso('E-mail alterado. Use o novo e-mail para entrar.');} catch(e){aviso(e.status===422?'Confira o e-mail.':e.message,true);} }; $('fSenha').onsubmit=async event=>{ event.preventDefault(); try{await api('/account/password',{current:$('dAtual').value,new:$('dNova').value}); $('dAtual').value='';$('dNova').value='';aviso('Senha alterada.');} catch(e){aviso(e.status===422?'A nova senha precisa ter 12 caracteres ou mais.':e.message,true);} }; // ---- Signing in ---------------------------------------------------------- async function migrateGuestCart(previous,next){ if(previous===next)return; const database=await new Promise((resolve,reject)=>{const r=indexedDB.open('dtf-local-cart',1);r.onupgradeneeded=()=>r.result.createObjectStore('cart');r.onsuccess=()=>resolve(r.result);r.onerror=()=>reject(r.error);}); await new Promise((resolve,reject)=>{const tx=database.transaction('cart','readwrite'),store=tx.objectStore('cart'),req=store.get(previous);req.onsuccess=()=>{if(req.result){store.put(req.result,next);store.delete(previous);}};tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);}); database.close(); } async function authenticate(path,body){ const previous=scope,guest=!conta; const r=await api(path,body);forgetCheckout(); const next=(await api('/session')).cart_scope;scope=next; if(guest)try{await migrateGuestCart(previous,next);}catch{aviso('Não foi possível transferir o carrinho salvo neste navegador.',true);} conta=r.customer;$('quem').textContent=conta.mail; $('password').value='';$('register-password').value=''; await mostra(); } $('login').onsubmit=async event=>{event.preventDefault();try{await authenticate('/account/login',{email:$('email').value,password:$('password').value});}catch(e){aviso(e.message,true);}}; $('register').onsubmit=async event=>{event.preventDefault();try{await authenticate('/account/register',{customer:{cnpj:$('cnpj').value,zap:$('phone').value,mail:$('register-email').value},password:$('register-password').value});}catch(e){aviso(e.status===422?'Confira o CNPJ, o WhatsApp e o e-mail. A senha precisa ter 12 caracteres ou mais.':e.message,true);}}; $('esqueci').onclick=()=>{$('esqueciTx').hidden=!$('esqueciTx').hidden;}; $('logout').onclick=async()=>{try{await window.dtfForgetPrivateData();await api('/account/logout',{});location.assign('/conta/entrar');}catch(error){aviso('Não foi possível limpar os dados deste navegador. Feche as abas do Site e limpe os dados do site nas configurações do navegador.',true);}}; (async()=>{ try{ const session=await api('/session');scope=session.cart_scope;environment=session.environment; $('dev-notice').hidden=environment!=='local'; conta=(await api('/account/me')).customer; if(conta)$('quem').textContent=conta.mail; await mostra(); }catch(error){aviso(error.message,true);} })();