/* 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);}
})();