Files
customerTicket/static/js/voip-call.js
T
2026-09-26 12:51:12 +03:30

120 lines
4.8 KiB
JavaScript

(()=>{
const roots=[...document.querySelectorAll('[data-voip-call]')];
if(!roots.length)return;
const csrfToken=()=>{
const item=document.cookie.split(';').map(value=>value.trim()).find(value=>value.startsWith('csrftoken='));
return item?decodeURIComponent(item.slice('csrftoken='.length)):'';
};
const menuFor=root=>root._voipMenu;
const statusFor=root=>root._voipStatus;
const positionMenu=root=>{
const menu=menuFor(root);
const trigger=root.querySelector('[data-voip-call-trigger]');
if(!menu||menu.hidden)return;
if(window.matchMedia('(max-width:560px)').matches){
menu.style.removeProperty('top');
menu.style.removeProperty('left');
return;
}
const rect=trigger.getBoundingClientRect();
const gap=10;
const edge=12;
const width=menu.offsetWidth;
const height=menu.offsetHeight;
const top=rect.bottom+gap+height<=window.innerHeight-edge?rect.bottom+gap:Math.max(edge,rect.top-height-gap);
const left=Math.min(Math.max(edge,rect.right-width),window.innerWidth-width-edge);
menu.style.top=`${Math.round(top)}px`;
menu.style.left=`${Math.round(left)}px`;
};
const setOpen=(root,open,{restoreFocus=false}={})=>{
const menu=menuFor(root);
const trigger=root.querySelector('[data-voip-call-trigger]');
if(!menu||!trigger)return;
menu.hidden=!open;
menu.classList.toggle('is-open',open);
trigger.classList.toggle('is-open',open);
trigger.setAttribute('aria-expanded',String(open));
if(open){
positionMenu(root);
requestAnimationFrame(()=>menu.querySelector('[data-voip-method]')?.focus());
}else{
menu.style.removeProperty('top');
menu.style.removeProperty('left');
if(restoreFocus)trigger.focus();
}
};
const closeOthers=current=>roots.forEach(root=>{if(root!==current)setOpen(root,false);});
const notify=(root,message,state)=>{
const status=statusFor(root);
clearTimeout(status._hideTimer);
status.textContent=message;
status.className=`voip-call-status is-visible is-${state}`;
if(state!=='loading')status._hideTimer=setTimeout(()=>status.classList.remove('is-visible'),4200);
};
const call=async(root,method)=>{
const trigger=root.querySelector('[data-voip-call-trigger]');
const buttons=[trigger,...menuFor(root).querySelectorAll('button')];
setOpen(root,false);
buttons.forEach(button=>button.disabled=true);
trigger.classList.add('is-loading');
notify(root,'در حال ارسال درخواست تماس…','loading');
try{
const response=await fetch(root.dataset.callUrl,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken(),'Accept':'application/json'},body:JSON.stringify({method})});
const data=await response.json().catch(()=>({}));
if(!response.ok||!data.success)throw new Error();
notify(root,data.message||'درخواست تماس با موفقیت ارسال شد.','success');
trigger.classList.add('is-success');
setTimeout(()=>trigger.classList.remove('is-success'),1800);
}catch(error){
notify(root,'برقراری تماس ناموفق بود.','error');
}finally{
trigger.classList.remove('is-loading');
buttons.forEach(button=>button.disabled=false);
}
};
roots.forEach((root,index)=>{
const menu=root.querySelector('[data-voip-call-menu]');
const status=root.querySelector('[data-voip-call-status]');
const trigger=root.querySelector('[data-voip-call-trigger]');
menu.id=`voip-call-menu-${index+1}`;
trigger.setAttribute('aria-controls',menu.id);
root._voipMenu=menu;
root._voipStatus=status;
menu._voipRoot=root;
document.body.append(menu,status);
});
document.addEventListener('click',event=>{
const methodButton=event.target.closest('[data-voip-method]');
if(methodButton){
const root=methodButton.closest('[data-voip-call-menu]')._voipRoot;
call(root,methodButton.dataset.voipMethod);
return;
}
const trigger=event.target.closest('[data-voip-call-trigger]');
if(trigger){
const root=trigger.closest('[data-voip-call]');
const methods=menuFor(root).querySelectorAll('[data-voip-method]');
if(methods.length===1){call(root,methods[0].dataset.voipMethod);return;}
const opening=menuFor(root).hidden;
closeOthers(root);
setOpen(root,opening);
return;
}
if(!event.target.closest('[data-voip-call-menu]'))roots.forEach(root=>setOpen(root,false));
});
document.addEventListener('keydown',event=>{
if(event.key==='Escape')roots.forEach(root=>{if(!menuFor(root).hidden)setOpen(root,false,{restoreFocus:true});});
});
window.addEventListener('resize',()=>roots.forEach(positionMenu));
window.addEventListener('scroll',()=>roots.forEach(positionMenu),true);
})();