All checks were successful
Build and deploy / Validate source (push) Successful in 6s
Build and deploy / Integration suite on a real stack (push) Successful in 2m12s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m35s
The PIX and card choices appeared under the cart, on the same page as the customer's details. "Ir para o pagamento" now sends the order and opens /pagamento, step 3 of the progress bar: the server's order summary, then PIX or card, each opening below. The cart keeps only the sending progress and its errors; a changed cart is sent again instead of offering the old quote. Portal links open the payment page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
78 lines
7.0 KiB
JavaScript
78 lines
7.0 KiB
JavaScript
const $=id=>document.getElementById(id);
|
|
const node=(tag,text)=>{const el=document.createElement(tag);if(text!==undefined)el.textContent=text;return el;};
|
|
const money=c=>(c/100).toLocaleString('pt-BR',{style:'currency',currency:'BRL'});
|
|
let scope,states={},environment='';
|
|
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;}
|
|
function button(text,fn){const b=node('button',text);b.onclick=async()=>{b.disabled=true;try{await fn();}catch(e){$('message').textContent=e.message;}finally{b.disabled=false;}};return b;}
|
|
function forgetCheckout(){for(const k of ['dtf-quote','dtf-request-key','dtf-request-body'])localStorage.removeItem(k);}
|
|
async function load(){
|
|
try{
|
|
const session=await api('/session');scope=session.cart_scope;environment=session.environment;
|
|
$('dev-notice').hidden=environment!=='local';
|
|
const account=await api('/account/me');
|
|
$('auth').hidden=!!account.customer;$('logout').hidden=!account.customer;
|
|
$('account').textContent=account.customer?'Conta: '+account.customer.mail:'';
|
|
$('guest').textContent=account.customer?'':'Você está vendo apenas os pedidos desta sessão. Crie uma conta para acessar seus pedidos em outro navegador.';
|
|
const data=await api('/customer/orders');states=data.states;
|
|
$('quotes').replaceChildren();
|
|
for(const quote of data.quotes){
|
|
const card=node('article');card.className='card';card.append(node('h3','Cotação '+quote.id.slice(0,8)));
|
|
card.append(node('p',quote.approved?'Total aprovado: '+money(quote.approved.total_cents):'Aguardando conferência de metragem e nota.'));
|
|
const link=node('a','Abrir cotação no Site');link.href='/pagamento?quote='+quote.id;card.append(link);$('quotes').append(card);
|
|
}
|
|
$('orders').replaceChildren();
|
|
if(!data.orders.length)$('orders').append(node('p','Nenhum pedido pago nesta conta ou sessão.'));
|
|
for(const order of data.orders){
|
|
const card=node('article');card.className='card';card.id='order-'+order.id;
|
|
card.append(node('h3','Pedido #'+order.number),node('p',states[order.state]+' · '+money(order.snapshot.total_cents)+(environment==='local'?' · pagamento de teste':'')));
|
|
card.append(node('p',new Date(order.created_at).toLocaleString('pt-BR')));
|
|
const content=node('div');
|
|
card.append(button('Ver detalhes e arquivos',()=>details(order.id,content)),content);$('orders').append(card);
|
|
if(new URLSearchParams(location.search).get('order')===order.id)await details(order.id,content);
|
|
}
|
|
}catch(error){$('message').textContent=error.message;}
|
|
}
|
|
async function details(id,container){
|
|
const order=await api('/customer/orders/'+id);container.replaceChildren();
|
|
for(const [i,item] of order.snapshot.items.entries())container.append(node('p',(i+1)+'. '+item.mode+' · '+item.billed_metres+' m · nota '+item.grade+' · '+money(item.total_cents)));
|
|
const history=node('ol');
|
|
for(const h of order.history)history.append(node('li',new Date(h.created_at).toLocaleString('pt-BR')+' · '+states[h.to_state]+(h.reason?' — '+h.reason:'')));
|
|
container.append(history);
|
|
for(const file of order.files){
|
|
const row=node('p',(file.kind==='final'?'Arquivo final':'Correção')+' · item '+(file.item_index+1)+' · '+file.name+' · '+(file.expired?'expirado':!file.active?'substituído':'disponível até '+new Date(file.expires_at).toLocaleDateString('pt-BR')));
|
|
if(file.active&&!file.expired)row.append(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();}));
|
|
container.append(row);
|
|
}
|
|
if(order.state==='cor'){
|
|
const form=node('form');form.append(node('h3','Enviar arte corrigida'));
|
|
const inputs=order.snapshot.items.map((item,index)=>{const label=node('label','Item '+(index+1)+' · '+item.mode);const input=node('input');input.type='file';input.multiple=true;input.dataset.item=index;label.append(input);form.append(label);return input;});
|
|
const note=node('textarea');note.placeholder='Descreva a correção';note.required=true;note.maxLength=1000;form.append(note);
|
|
const submit=node('button','Enviar correção');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=>$('message').textContent=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});
|
|
$('message').textContent='Correção enviada. Aguarde a conferência da equipe.';await details(id,container);
|
|
}catch(error){$('message').textContent=error.message;}finally{submit.disabled=false;}};
|
|
container.append(form);
|
|
}
|
|
}
|
|
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=!(await api('/account/me')).customer;
|
|
await api(path,body);forgetCheckout();$('message').textContent='Acesso confirmado.';
|
|
const next=(await api('/session')).cart_scope;
|
|
if(guest)try{await migrateGuestCart(previous,next);}catch{$('message').textContent='Acesso confirmado. Não foi possível transferir o carrinho salvo neste navegador.';}
|
|
await load();$('password').value='';$('register-password').value='';
|
|
}
|
|
$('login').onsubmit=async event=>{event.preventDefault();try{await authenticate('/account/login',{email:$('email').value,password:$('password').value});}catch(e){$('message').textContent=e.message;}};
|
|
$('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){$('message').textContent=e.message;}};
|
|
$('logout').onclick=async()=>{try{await window.dtfForgetPrivateData();await api('/account/logout',{});location.reload();}catch(error){$('message').textContent='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.';}};
|
|
$('refresh').onclick=load;
|
|
load();
|