Files
baiyue-logistics-chat/static/js/overview.js
T

131 lines
5.9 KiB
JavaScript

// Display a fresh, customer-scoped order overview after login or session recovery.
var CustomerOverview = {
create(options){
const panel=document.getElementById('customer-overview');
const title=document.getElementById('customer-overview-title');
const summary=document.getElementById('customer-overview-summary');
const body=document.getElementById('customer-overview-body');
let user=null;
let overview=null;
let failure='';
let loading=false;
let requestVersion=0;
let controller=null;
panel.hidden=true;
function element(tag,className,text){
const node=document.createElement(tag);
if(className) node.className=className;
if(text !== undefined) node.textContent=text;
return node;
}
function queryButton(label,question,className){
const button=element('button',className,label);
button.type='button';
button.onclick=function(){
panel.open=false;
options.onQuery(question);
};
return button;
}
function render(){
title.textContent=options.text('overviewTitle');
body.innerHTML='';
panel.hidden=!user;
if(!user){summary.textContent='';return;}
if(!overview){
const key=loading ? 'overviewLoading' : failure==='unlinked' ? 'overviewUnlinked' : 'overviewFailed';
summary.textContent=options.text(key);
}else{
summary.textContent=options.text('overviewProcessing')+' '+overview.processing+' · '+options.text('overviewExceptions')+' '+overview.exceptions;
const metrics=element('div','overview-metrics');
for(const [key,count,question] of [
['overviewTotal',overview.total,options.text('overviewAllQuery')],
['overviewProcessing',overview.processing,options.text('overviewProcessingQuery')],
['overviewExceptions',overview.exceptions,options.text('exceptionQuery')]
]){
const button=queryButton('',question,'overview-metric');
button.append(element('strong','',String(count)),element('span','',options.text(key)));
metrics.appendChild(button);
}
body.appendChild(metrics);
const stages=element('div','overview-stages');
const labels=options.text('overviewStageLabels');
for(const stage of overview.stages){
stages.appendChild(element('span','',labels[stage.code]+' '+stage.count));
}
body.appendChild(stages);
const orders=element('div','overview-orders');
for(const order of overview.orders){
const button=element('button','overview-order');
button.type='button';
const reference=order.inbound_code || order.order_no;
button.appendChild(element('strong','overview-reference',reference));
button.appendChild(element('span','overview-stage',labels[order.stage]));
if(order.exception_reason){
button.classList.add('has-exception');
button.appendChild(element('span','overview-reason',order.exception_reason));
}
button.onclick=function(){options.onLookup(order,button);};
orders.appendChild(button);
}
if(!overview.orders.length) orders.appendChild(element('p','overview-empty',options.text('overviewEmpty')));
body.appendChild(orders);
}
const refresh=element('button','overview-refresh',options.text(loading ? 'overviewLoading' : 'overviewRefresh'));
refresh.type='button';
refresh.disabled=loading;
refresh.onclick=refreshOverview;
body.appendChild(refresh);
}
async function refreshOverview(){
if(!user || loading) return;
const token=++requestVersion;
const customerId=user.id;
controller=new AbortController();
const activeController=controller;
const timer=setTimeout(()=>activeController.abort(),10000);
loading=true;
failure='';
render();
try{
const response=await options.fetch('/api/customer/session?overview=1',{signal:activeController.signal});
const result=await response.json();
if(token!==requestVersion || !user || user.id!==customerId) return;
if(!response.ok || !result.ok) throw new Error('Overview unavailable');
if(!result.authenticated || !result.user || result.user.id!==customerId){
setUser(null);
if(options.onExpired) options.onExpired();
return;
}
overview=result.overview || null;
failure=result.overview_error || (overview ? '' : 'unavailable');
}catch(error){
if(token===requestVersion){overview=null;failure='unavailable';}
}finally{
clearTimeout(timer);
if(token===requestVersion){controller=null;loading=false;render();}
}
}
function setUser(next){
if(user && next && user.id===next.id) return;
requestVersion+=1;
if(controller) controller.abort();
controller=null;
user=next;
overview=null;
failure='';
loading=false;
panel.open=Boolean(user);
render();
if(user) refreshOverview();
}
return {setUser:setUser,render:render};
}
};