124 lines
6.3 KiB
JavaScript
124 lines
6.3 KiB
JavaScript
// Customer login has no relationship to the protected staff workspace.
|
|
var CustomerLogin = {
|
|
create(options){
|
|
const overlay=document.getElementById('customer-login');
|
|
const form=document.getElementById('customer-login-form');
|
|
const username=document.getElementById('customer-username');
|
|
const password=document.getElementById('customer-password');
|
|
const submit=document.getElementById('customer-submit');
|
|
const cancel=document.getElementById('customer-cancel');
|
|
const error=document.getElementById('customer-error');
|
|
const account=document.getElementById('customer-account');
|
|
let user, pending=null, opener=null, busy=false, version=0;
|
|
overlay.hidden=true;
|
|
function status(message){ error.textContent=message || ''; error.hidden=!message; }
|
|
function render(){
|
|
account.textContent=user ? user.name+' · '+options.text('customerLogout') : options.text('customerLogin');
|
|
submit.textContent=options.text(busy ? 'customerSigningIn' : 'customerLogin');
|
|
cancel.textContent=options.text('customerCancel');
|
|
document.getElementById('customer-login-title').textContent=options.text('customerLogin');
|
|
document.getElementById('customer-username-label').textContent=options.text('customerUsername');
|
|
document.getElementById('customer-password-label').textContent=options.text('customerPassword');
|
|
username.placeholder=options.text('customerUsernamePlaceholder');
|
|
password.placeholder=options.text('customerPasswordPlaceholder');
|
|
}
|
|
function update(next){
|
|
const previous=user;
|
|
user=next;
|
|
render();
|
|
if(options.onChange && (previous === undefined || (previous && previous.id) !== (user && user.id))){
|
|
options.onChange(user,previous);
|
|
}
|
|
}
|
|
async function request(path, data){
|
|
const controller=new AbortController();
|
|
const timer=setTimeout(()=>controller.abort(),10000);
|
|
try{
|
|
const response=await options.fetch(path,{method:data === undefined ? 'GET' : 'POST',
|
|
headers:{'Content-Type':'application/json'},body:data === undefined ? undefined : JSON.stringify(data),signal:controller.signal});
|
|
const result=await response.json();
|
|
if(!response.ok || !result.ok){
|
|
const failure=new Error(result.error || result.e || options.text('customerLoginFailed'));
|
|
failure.publicMessage=true;throw failure;
|
|
}
|
|
return result;
|
|
}finally{ clearTimeout(timer); }
|
|
}
|
|
async function refresh(){
|
|
if(busy) return;
|
|
const token=++version;
|
|
try{
|
|
const result=await request('/api/customer/session');
|
|
if(token === version) update(result.authenticated && result.user ? result.user : null);
|
|
}catch(error){} // Query endpoints still enforce login if status cannot refresh.
|
|
}
|
|
function close(){
|
|
overlay.hidden=true;
|
|
password.value='';
|
|
pending=null;
|
|
if(opener && opener.isConnected) opener.focus({preventScroll:true});
|
|
opener=null;
|
|
}
|
|
function open(onSuccess){
|
|
if(busy) return;
|
|
pending=typeof onSuccess === 'function' ? onSuccess : null;
|
|
opener=document.activeElement;
|
|
status(''); password.value=''; overlay.hidden=false;
|
|
if(options.onOpen) options.onOpen();
|
|
render(); username.focus({preventScroll:true});
|
|
}
|
|
function setBusy(value){
|
|
busy=value; submit.disabled=value; cancel.disabled=value; account.disabled=value;
|
|
username.disabled=value; password.disabled=value; render();
|
|
}
|
|
form.addEventListener('submit',async function(event){
|
|
event.preventDefault();
|
|
if(busy) return;
|
|
const token=++version;
|
|
setBusy(true);status('');
|
|
try{
|
|
const result=await request('/api/customer/login',{username:username.value,password:password.value});
|
|
if(token !== version) return;
|
|
update(result.user);
|
|
const resume=pending;close();
|
|
if(resume) await resume();
|
|
}catch(failure){
|
|
if(token === version) status(failure.publicMessage ? failure.message : options.text('customerLoginFailed'));
|
|
}finally{ if(token === version) setBusy(false); }
|
|
});
|
|
cancel.onclick=function(){ if(!busy) close(); };
|
|
overlay.addEventListener('click',function(event){ if(event.target === overlay && !busy) close(); });
|
|
overlay.addEventListener('keydown',function(event){
|
|
if(event.key === 'Escape' && !busy){ event.preventDefault();close(); }
|
|
if(event.key === 'Tab'){
|
|
const controls=[username,password,submit,cancel].filter(control=>!control.disabled);
|
|
if(!controls.length){ event.preventDefault();return; }
|
|
const index=controls.indexOf(document.activeElement);
|
|
const next=(index+(event.shiftKey ? -1 : 1)+controls.length)%controls.length;
|
|
event.preventDefault();controls[next].focus({preventScroll:true});
|
|
}
|
|
});
|
|
account.onclick=async function(){
|
|
if(busy) return;
|
|
if(!user){ open(); return; }
|
|
const token=++version;setBusy(true);
|
|
try{
|
|
await request('/api/customer/logout',{});
|
|
if(token === version) update(null);
|
|
}catch(failure){ if(options.onError) options.onError(options.text('customerLogoutFailed')); }
|
|
finally{ if(token === version) setBusy(false); }
|
|
};
|
|
window.addEventListener('focus',refresh);
|
|
document.addEventListener('visibilitychange',function(){ if(!document.hidden) refresh(); });
|
|
render();
|
|
return {open:open,refresh:refresh,render:render,require:async function(resume){
|
|
if(busy) return;
|
|
await refresh();
|
|
if(user){ await resume(); }else open(resume);
|
|
},viewport:function(height,top){
|
|
overlay.style.height=height || ''; overlay.style.top=top || '';
|
|
overlay.style.bottom=height ? 'auto' : '';
|
|
}};
|
|
}
|
|
};
|