/* Server remains authoritative. Display Jalali, submit an ASCII Gregorian date-only. No dates, bank details or receipts are placed in localStorage/service-worker caches. */ (() => { 'use strict'; const form=document.querySelector('#receipt-form');if(!form)return; const q=name=>form.elements.namedItem(name),button=document.querySelector('#submit-receipt'); const notices=JSON.parse(document.querySelector('#notice-data').textContent); const day=q('paid_day'),month=q('paid_month'),year=q('paid_year'); const digits=value=>String(value).replace(/[۰-۹]/g,x=>String(x.charCodeAt(0)-1776)).replace(/[٠-٩]/g,x=>String(x.charCodeAt(0)-1632)); const formatter=new Intl.DateTimeFormat('en-US',{calendar:'persian',numberingSystem:'latn',timeZone:'Asia/Tehran',year:'numeric',month:'numeric',day:'numeric'}); const parts=date=>Object.fromEntries(formatter.formatToParts(date).map(p=>[p.type,p.value])); function newYear(y){for(let d=18;d<=24;d++){const date=new Date(Date.UTC(y+621,2,d,12)),p=parts(date);if(Number(p.year)===y && Number(p.month)===1 && Number(p.day)===1)return date;}throw Error('تاریخ معتبر نیست.');} function dateValue(){ const output=document.querySelector('#paid-date-iso');output.value=''; if(!day.value || !month.value || !year.value)return ''; try{const y=Number(year.value),m=Number(month.value),d=Number(day.value),offset=m<=6?(m-1)*31:186+(m-7)*30;const date=new Date(newYear(y).getTime()+(offset+d-1)*86400000),p=parts(date);if(Number(p.year)!==y || Number(p.month)!==m || Number(p.day)!==d)return '';output.value=date.toISOString().slice(0,10);return output.value;}catch{return '';} } function dateUpdate(){const iso=dateValue();document.querySelector('#date-preview').textContent=iso?'تاریخ انتخاب‌شده: '+day.options[day.selectedIndex].text+' '+month.options[month.selectedIndex].text+' '+year.value:'روز، ماه و سال پرداخت را کامل انتخاب کنید.';} function monthUpdate(){ let length=31; if(month.value && year.value){const m=Number(month.value),y=Number(year.value);length=m<=6?31:m<=11?30:(newYear(y+1)-newYear(y))/86400000===366?30:29;} [...day.options].forEach(option=>{option.disabled=Number(option.value)>length;});if(Number(day.value)>length)day.value='';dateUpdate(); } [month,year].forEach(control=>control.addEventListener('change',monthUpdate));day.addEventListener('change',dateUpdate); document.querySelector('#date-today').addEventListener('click',()=>{const p=parts(new Date());year.value=p.year;month.value=p.month;monthUpdate();day.value=p.day;dateUpdate();day.focus();});monthUpdate(); function sync(){ const linked=q('receipt_mode').value==='linked';const select=q('notice_id');form.querySelector('[data-field=notice_id]').hidden=!linked;select.disabled=!linked;select.required=linked; if(!linked)select.value=''; const notice=linked?notices.find(n=>n.id===select.value):null;const summary=document.querySelector('#selected-payment');summary.hidden=!notice; const summaryText=notice?'مبلغ اعلام‌شده: '+new Intl.NumberFormat('fa-IR').format(BigInt(notice.amount))+' ریال · شناسه واریز: '+(notice.deposit || 'اعلام نشده')+(notice.invoice?' · فاکتور: '+notice.invoice:''):''; const title=summary.querySelector('[data-selected-notice-title]'); if(title){title.textContent=notice?notice.subject:'';summary.querySelector('[data-selected-notice-summary]').textContent=summaryText;summary.querySelector('[data-selected-notice-link]').href=notice?notice.url:'#';}else summary.textContent=summaryText; const finalSummary=document.querySelector('#receipt-summary');if(finalSummary)finalSummary.textContent=notice?'این فیش برای «'+notice.subject+'» ارسال می‌شود.':linked?'اعلامیه مرتبط را انتخاب کنید.':'این فیش به صورت واریز مستقل برای بررسی ارسال می‌شود.'; const depositWarning=summary.querySelector('[data-selected-deposit-warning]'); depositWarning.hidden=!notice?.deposit; q('deposit_id').classList.toggle('pay-deposit-emphasis',Boolean(notice?.deposit)); q('deposit_id').required=false; } form.querySelectorAll('[name=receipt_mode]').forEach(control=>control.addEventListener('change',sync));q('notice_id').addEventListener('change',()=>{const notice=notices.find(n=>n.id===q('notice_id').value);if(notice){q('destination_account').value=String(notice.account);q('deposit_id').value=notice.deposit;}sync();});q('destination_account').addEventListener('change',sync);sync(); const fields=[...form.querySelectorAll('[data-field]')]; fields.forEach(field=>{const controls=field.querySelectorAll('input,select,textarea');controls.forEach(control=>{control.setAttribute('aria-describedby','error-'+field.dataset.field);if(field.querySelector('.field-error').textContent.trim())control.setAttribute('aria-invalid','true');});}); let busy=false; function showErrors(errors){ fields.forEach(field=>{const name=field.dataset.field,message=errors.get(name)||'',error=field.querySelector('.field-error');error.textContent=message;error.hidden=!message;field.querySelectorAll('input,select,textarea').forEach(control=>control.setAttribute('aria-invalid',String(Boolean(message))));}); const summary=document.querySelector('#receipt-errors'),list=summary.querySelector('ul');list.replaceChildren();errors.forEach((message,name)=>{const item=document.createElement('li'),link=document.createElement('a');link.href='#id_'+name;link.textContent=message;link.onclick=event=>{event.preventDefault();q(name).focus();q(name).scrollIntoView({block:'center'});};item.append(link);list.append(item);});summary.hidden=!errors.size; if(errors.size){const first=q(errors.keys().next().value);first.focus();first.scrollIntoView({block:'center'});} } async function validateImage(file){ if(!file)return 'تصویر فیش واریز را انتخاب کنید.'; if(!file.size || file.size>Number(form.dataset.maxBytes))return 'حجم تصویر بیش از حد مجاز است یا تصویر خالی است.'; const extension=file.name.split('.').pop().toLowerCase();if(!['jpg','jpeg','png','gif','webp'].includes(extension))return 'فقط تصویر JPG، PNG، GIF یا WEBP انتخاب کنید.'; const readable=await new Promise(resolve=>{const picture=new Image(),url=URL.createObjectURL(file);picture.onload=()=>{URL.revokeObjectURL(url);resolve(picture.naturalWidth*picture.naturalHeight<=25000000);};picture.onerror=()=>{URL.revokeObjectURL(url);resolve(false);};picture.src=url;});return readable?'':'تصویر باز نمی‌شود یا ابعاد آن بیش از حد مجاز است.'; } let revision=0;form.addEventListener('input',()=>revision++);form.addEventListener('change',()=>revision++); form.addEventListener('submit',async event=>{ event.preventDefault();if(busy)return;busy=true;button.disabled=true;const version=revision,errors=new Map(); try{ const value=digits(q('paid_amount').value).trim();if(!/^\d+$/.test(value) && !/^\d{1,3}(?:[,٬ ]\d{3})+$/.test(value))errors.set('paid_amount','مبلغ را به ریال و با عدد صحیح وارد کنید.');else{const amount=BigInt(value.replace(/[,٬ ]/g,''));if(amount<=0n || amount>9007199254740991n)errors.set('paid_amount','مبلغ باید مثبت و در محدوده مجاز باشد.');} if(q('receipt_mode').value==='linked' && !q('notice_id').value)errors.set('notice_id','اعلامیه را انتخاب کنید یا واریز مستقل را بزنید.'); if(!q('destination_account').value)errors.set('destination_account','حساب مقصد را انتخاب کنید.'); const iso=dateValue();if(!iso)errors.set('paid_day','تاریخ را کامل و معتبر انتخاب کنید؛ روز، ماه و سال لازم‌اند.'); else{const now=Object.fromEntries(new Intl.DateTimeFormat('en-US',{timeZone:'Asia/Tehran',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date()).map(p=>[p.type,p.value]));if(iso>[now.year,now.month,now.day].join('-'))errors.set('paid_day','تاریخ واریز نمی‌تواند بعد از امروز باشد.');} const imageError=await validateImage(q('receipt_image').files[0]);if(imageError)errors.set('receipt_image',imageError); if(version!==revision){document.querySelector('#form-status').textContent='اطلاعات تغییر کرده است؛ دوباره ارسال را بزنید.';return;} showErrors(errors);if(errors.size)return; button.textContent='در حال ثبت…';HTMLFormElement.prototype.submit.call(form);return; }finally{busy=false;if(button.textContent!=='در حال ثبت…')button.disabled=false;} }); if(document.querySelector('#receipt-errors').hidden===false){const field=form.querySelector('[aria-invalid=true]');if(field){field.focus();field.scrollIntoView({block:'center'});}} })();