Files
2026-10-07 13:39:29 +03:30

66 lines
4.9 KiB
JavaScript

/* Real multipart upload progress; registration remains authoritative on the server. */
(() => {
'use strict';
const number=new Intl.NumberFormat('fa-IR',{maximumFractionDigits:1});
function bytes(value){return value>=1048576?number.format(value/1048576)+' مگابایت':number.format(value/1024)+' کیلوبایت';}
function duration(seconds){return seconds>=60?number.format(Math.ceil(seconds/60))+' دقیقه':number.format(Math.max(1,Math.ceil(seconds)))+' ثانیه';}
function send(form){
const payload=new FormData(form),panel=document.querySelector('#receipt-upload');
const title=document.querySelector('#receipt-upload-title'),meter=document.querySelector('#receipt-upload-progress');
const percent=document.querySelector('#receipt-upload-percent'),amount=document.querySelector('#receipt-upload-bytes'),time=document.querySelector('#receipt-upload-time');
const button=document.querySelector('#submit-receipt');
const controls=[...form.querySelectorAll('input,select,textarea,button')].map(control=>[control,control.disabled]);
controls.forEach(([control])=>{control.disabled=true;});form.classList.add('is-uploading');button.setAttribute('aria-busy','true');
panel.hidden=false;title.textContent='در حال ارسال فیش…';button.textContent='در حال ارسال…';
meter.value=0;percent.textContent='۰٪';amount.textContent='در انتظار شروع ارسال';
const started=performance.now();let loaded=0,total=0,lastProgress=started,processing=false;
function updateTime(){
if(processing)return;
const elapsed=(performance.now()-started)/1000;
const estimate=loaded>0 && elapsed>=1 && total>loaded;
time.textContent='زمان سپری‌شده: '+number.format(Math.floor(elapsed))+' ثانیه'+(performance.now()-lastProgress>5000?' · منتظر ادامه ارسال؛ سرعت اتصال تغییر کرده است.':estimate?' · حدود '+duration((total-loaded)*elapsed/loaded)+' دیگر برای ارسال':' · در حال محاسبه زمان باقی‌مانده…');
}
function process(){
processing=true;meter.value=100;percent.textContent='۱۰۰٪';
title.textContent='ارسال کامل شد؛ در حال بررسی و ثبت فیش…';button.textContent='در حال ثبت…';
time.textContent='منتظر تأیید ثبت از سرور؛ زمان این مرحله به سرعت پردازش بستگی دارد.';
}
updateTime();panel.scrollIntoView({block:'nearest'});
return new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest(),timer=setInterval(updateTime,1000);let settled=false;
function finish(error,result){
if(settled)return;settled=true;clearInterval(timer);
if(!result?.redirect_url){
controls.forEach(([control,disabled])=>{control.disabled=disabled;});form.classList.remove('is-uploading');button.removeAttribute('aria-busy');
title.textContent='ثبت فیش انجام نشد';time.textContent='اطلاعات و تصویر حفظ شده‌اند؛ پس از رفع مشکل دوباره ارسال کنید.';
}
if(error)reject(error);else resolve(result);
}
xhr.upload.addEventListener('progress',event=>{
loaded=event.loaded;total=event.lengthComputable?event.total:0;lastProgress=performance.now();
amount.textContent='ارسال‌شده: '+bytes(loaded)+(total?' از '+bytes(total):'');
if(total){const value=Math.min(100,Math.floor(loaded/total*100));meter.value=value;percent.textContent=number.format(value)+'٪';}
else{meter.removeAttribute('value');percent.textContent='در حال ارسال';}
updateTime();
});
xhr.upload.addEventListener('load',process);
xhr.addEventListener('load',()=>{
const result=xhr.response;
if(xhr.status>=200 && xhr.status<300 && result?.redirect_url){finish(null,result);return;}
if(xhr.status===422 && result?.errors){finish(null,result);return;}
const message=[401,403,410].includes(xhr.status)?'دسترسی یا نشست شما معتبر نیست؛ صفحه را دوباره باز کنید.':xhr.status===413?'حجم تصویر بیش از حد مجاز سرور است؛ تصویر کوچک‌تری انتخاب کنید.':'ثبت فیش تأیید نشد؛ دوباره تلاش کنید. در صورت تکرار، وضعیت فیش‌های خود را بررسی کنید.';
finish(new Error(message));
});
xhr.addEventListener('error',()=>finish(new Error('ارتباط با سرور قطع شد؛ اتصال اینترنت را بررسی و دوباره ارسال کنید.')));
xhr.addEventListener('abort',()=>finish(new Error('ارسال متوقف شد؛ دوباره تلاش کنید.')));
try{
xhr.open('POST',form.action || location.href);xhr.responseType='json';
xhr.setRequestHeader('Accept','application/json');
xhr.setRequestHeader('X-CSRFToken',payload.get('csrfmiddlewaretoken'));
xhr.send(payload);
}catch(error){finish(error);}
});
}
window.ReceiptUpload=Object.freeze({send});
})();