160 lines
11 KiB
JavaScript
160 lines
11 KiB
JavaScript
/* Local UI prototype. No requests, storage, upload or database writes.
|
|
Keep opaque notice IDs in links. Production must check ownership server-side. */
|
|
(() => {
|
|
'use strict';
|
|
const version=document.documentElement.dataset.staticVersion;
|
|
const notices=window.chaharsuPaymentNotices || [];
|
|
const byId=id=>notices.find(notice=>notice.id===id);
|
|
const query=selector=>document.querySelector(selector);
|
|
const money=notice=>new Intl.NumberFormat('fa-IR').format(Number(notice.amount))+' '+notice.currency;
|
|
const fields=notice=>[
|
|
['مبلغ واریز',money(notice),notice.amount,true],
|
|
['نام بانک',notice.bank,notice.bank,false],
|
|
['صاحب حساب',notice.holder,notice.holder,false],
|
|
['شماره حساب',notice.account,notice.account,true],
|
|
['شماره شبا',notice.iban,notice.iban,true],
|
|
['شماره کارت',notice.card || 'اعلام نشده',notice.card,true],
|
|
['شناسهٔ واریز',notice.deposit || 'اعلام نشده',notice.deposit,true],
|
|
['شماره فاکتور',notice.invoice || 'اعلام نشده',notice.invoice,true],
|
|
['توضیحات کارشناس',notice.description || 'توضیحی ثبت نشده',notice.description,false]
|
|
];
|
|
const link=(page,id)=>page+'?v='+version+(id?'¬ice='+encodeURIComponent(id):'');
|
|
function copyTextFor(notice){
|
|
return ['اطلاعات نمایشی چهارسو — شمارهها ساختگی و غیرقابل پرداختاند.',notice.title,
|
|
'شمارهٔ اعلامیه: '+notice.id,'تاریخ اعلام: '+notice.issued,'اعلامکننده: '+notice.author,
|
|
...fields(notice).map(([label,value])=>label+': '+value)].join('\n');
|
|
}
|
|
let feedbackTimer;
|
|
function feedback(message){
|
|
clearTimeout(feedbackTimer);query('#copy-feedback').textContent=message;
|
|
feedbackTimer=setTimeout(()=>{query('#copy-feedback').textContent='';},6500);
|
|
}
|
|
function fallbackCopy(text){
|
|
const area=document.createElement('textarea');area.value=text;area.setAttribute('aria-label','متن کپی');
|
|
area.style.cssText='position:fixed;top:0;left:0;width:1px;height:1px;opacity:0';
|
|
document.body.append(area);area.select();let copied=false;
|
|
try{copied=document.execCommand('copy');}catch(error){copied=false;}finally{area.remove();}
|
|
return copied;
|
|
}
|
|
async function copyValue(text,label){
|
|
const previousFocus=document.activeElement;
|
|
let copied=false;
|
|
try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(text);copied=true;}}catch(error){copied=false;}
|
|
if(!copied)copied=fallbackCopy(text);
|
|
previousFocus?.focus({preventScroll:true});
|
|
if(copied){feedback(label+' کپی شد. اکنون میتوانید آن را جایگذاری کنید.');return;}
|
|
feedback('کپی خودکار ممکن نشد؛ متن آمادهٔ انتخاب است.');
|
|
const dialog=query('#manual-copy');query('#manual-copy-text').value=text;
|
|
dialog.showModal();query('#manual-copy-text').focus();query('#manual-copy-text').select();
|
|
}
|
|
function appendField(container,field,copyable=true){
|
|
const [label,value,raw,ltr]=field;
|
|
const row=document.createElement('div');row.className='payment-field';
|
|
const term=document.createElement('dt');term.textContent=label;
|
|
const definition=document.createElement('dd');definition.textContent=value;
|
|
if(ltr){definition.dir='ltr';definition.className='numeric-value';}
|
|
row.append(term,definition);
|
|
if(copyable && raw){
|
|
const button=document.createElement('button');button.type='button';button.className='copy-field';
|
|
button.textContent='کپی';button.setAttribute('aria-label','کپی '+label);
|
|
button.addEventListener('click',()=>copyValue(raw,label));row.append(button);
|
|
}else{row.classList.add('no-copy');}
|
|
container.append(row);
|
|
}
|
|
function makeNotice(notice){
|
|
const article=query('#notice-template').content.firstElementChild.cloneNode(true);
|
|
const button=article.querySelector('.notice-toggle');const detail=article.querySelector('.notice-detail');
|
|
article.id=notice.id;button.id='toggle-'+notice.id;detail.id='details-'+notice.id;
|
|
button.setAttribute('aria-controls',detail.id);detail.setAttribute('aria-labelledby',button.id);
|
|
article.querySelector('.notice-title').textContent=notice.title;
|
|
article.querySelector('.notice-amount').textContent=money(notice);
|
|
article.querySelector('.notice-meta').textContent='فاکتور '+notice.invoice+' · '+notice.issued;
|
|
const state=article.querySelector('.payment-state');
|
|
state.textContent=notice.receiptReceived?'فیش در حال بررسی':'در انتظار فیش';
|
|
state.classList.toggle('received',notice.receiptReceived);
|
|
article.querySelector('.notice-author').textContent='اعلامشده توسط '+notice.author;
|
|
article.querySelector('.notice-date').textContent='اعلامیهٔ '+notice.id;
|
|
article.querySelector('.notice-bank-name').textContent=notice.bank;
|
|
article.querySelector('.notice-holder').textContent=notice.holder;
|
|
const container=article.querySelector('.essential-fields');
|
|
// Compact UI is a projection; copyTextFor always uses the full notice, including all supplementary information.
|
|
const allFields=fields(notice);
|
|
[0,3,4,5,6].forEach(index=>{const field=allFields[index];if(field[2])appendField(container,field);});
|
|
const extra=article.querySelector('.extra-fields');
|
|
[7,8].forEach(index=>appendField(extra,allFields[index],false));
|
|
article.querySelector('.copy-complete').addEventListener('click',()=>copyValue(copyTextFor(notice),'اطلاعات کامل'));
|
|
article.querySelector('.receipt-link').href=link('payment-receipt.html',notice.id);
|
|
button.addEventListener('click',()=>toggleNotice(article,notice));
|
|
return article;
|
|
}
|
|
function openNotice(article){
|
|
document.querySelectorAll('.payment-notice').forEach(item=>{
|
|
const open=item===article;item.querySelector('.notice-toggle').setAttribute('aria-expanded',String(open));
|
|
item.querySelector('.notice-detail').hidden=!open;
|
|
});
|
|
}
|
|
function toggleNotice(article,notice){
|
|
const open=article.querySelector('.notice-toggle').getAttribute('aria-expanded')==='true';
|
|
openNotice(open?null:article);
|
|
const url=new URL(location.href);url.searchParams.delete('notice');
|
|
if(!open)url.searchParams.set('notice',notice.id);
|
|
history.replaceState(null,'',url.href);
|
|
}
|
|
function initAccounts(){
|
|
const container=query('#payment-notices');notices.forEach(notice=>container.append(makeNotice(notice)));
|
|
const search=query('#notice-search');
|
|
function filter(){
|
|
const term=search.value.trim().toLocaleLowerCase('fa');let count=0;
|
|
notices.forEach(notice=>{
|
|
const article=document.getElementById(notice.id);
|
|
const match=[notice.title,notice.invoice,notice.bank,notice.id].join(' ').toLocaleLowerCase('fa').includes(term);
|
|
article.hidden=!match;if(match)count++;
|
|
});
|
|
query('#notice-count').textContent=new Intl.NumberFormat('fa-IR').format(count)+' مورد';
|
|
query('#notices-empty').hidden=count!==0;
|
|
}
|
|
search.addEventListener('input',filter);
|
|
query('#clear-search').addEventListener('click',()=>{search.value='';filter();search.focus();});filter();
|
|
if(!notices.length){query('#notices-empty h3').textContent='هنوز اطلاعات واریزی برای شما ارسال نشده است';query('#notices-empty p').textContent='پس از اعلام کارشناس، اطلاعات هر واریز در این بخش نمایش داده میشود.';query('#clear-search').hidden=true;search.disabled=true;}
|
|
const id=new URLSearchParams(location.search).get('notice');
|
|
if(id){const notice=byId(id);if(notice){openNotice(document.getElementById(id));}else{feedback('این اعلامیه در دسترس نیست. فقط موارد اعلامشده به شما نمایش داده میشوند.');}}
|
|
}
|
|
function initReceipt(){
|
|
const select=query('#notice-select');
|
|
notices.forEach(notice=>{const option=document.createElement('option');option.value=notice.id;option.textContent=notice.invoice+' · '+money(notice)+' · '+notice.bank;select.append(option);});
|
|
if(!notices.length)query('#notice-selection-help').textContent='هنوز اعلامیهای ندارید؛ از کارشناس بخواهید ایجاد کند یا واریز مستقل را انتخاب کنید.';
|
|
const modes=document.querySelectorAll('input[name="receipt-mode"]');
|
|
let invalid=false;
|
|
function sync(){
|
|
const linked=query('input[name="receipt-mode"]:checked').value==='linked';
|
|
query('#linked-payment').hidden=!linked;query('#independent-help').hidden=linked;
|
|
select.disabled=!linked;select.required=linked;
|
|
if(!linked){select.value='';invalid=false;}
|
|
const notice=linked?byId(select.value):null;
|
|
query('#notice-invalid').hidden=!invalid;
|
|
select.setAttribute('aria-invalid',String(invalid));
|
|
query('#selected-payment').hidden=!notice;
|
|
query('#selected-fields').replaceChildren();
|
|
if(notice){
|
|
query('#selected-title').textContent=notice.title;
|
|
[['مبلغ اعلامشده',money(notice),false],['شماره فاکتور',notice.invoice,true],['بانک مقصد',notice.bank,false],['شناسهٔ واریز',notice.deposit || 'اعلام نشده',true]].forEach(([label,value,ltr])=>appendField(query('#selected-fields'),[label,value,null,ltr],false));
|
|
query('#selected-details').href=link('bank-accounts.html',notice.id);
|
|
}
|
|
query('#receipt-summary').textContent=linked?(notice?'این فیش به فاکتور '+notice.invoice+' مرتبط میشود.':'برای فیش مرتبط، ابتدا واریز اعلامشده را انتخاب کنید.'):'نوع فیش: واریز مستقل؛ ارتباطی با اعلامیههای قبلی ندارد.';
|
|
const url=new URL(location.href);url.searchParams.delete('notice');if(notice)url.searchParams.set('notice',notice.id);
|
|
history.replaceState(null,'',url.href);
|
|
}
|
|
const incoming=new URLSearchParams(location.search).get('notice');
|
|
if(incoming){query('input[value="linked"]').checked=true;if(byId(incoming)){select.value=incoming;}else{invalid=true;}}
|
|
modes.forEach(mode=>mode.addEventListener('change',sync));select.addEventListener('change',()=>{invalid=false;sync();});sync();
|
|
const input=query('#receipt-file');const selection=query('#file-selection');const status=query('#file-status');
|
|
input.addEventListener('change',()=>{const file=input.files[0];selection.hidden=!file;status.textContent=file?'فایل انتخابشده: '+file.name+' · ارسال نشده':'';});
|
|
query('#clear-file').addEventListener('click',()=>{input.value='';selection.hidden=true;status.textContent='';input.focus();});
|
|
query('#receipt-form').addEventListener('submit',event=>event.preventDefault());
|
|
}
|
|
query('#select-copy-text').addEventListener('click',()=>{query('#manual-copy-text').focus();query('#manual-copy-text').select();});
|
|
query('#close-copy').addEventListener('click',()=>query('#manual-copy').close());
|
|
if(document.body.dataset.financePage==='accounts')initAccounts();
|
|
if(document.body.dataset.financePage==='receipt')initReceipt();
|
|
})();
|