95 lines
5.6 KiB
JavaScript
95 lines
5.6 KiB
JavaScript
/* Local, server-filtered search. Explicit choice only; no customer data in storage. */
|
|
(() => {
|
|
'use strict';
|
|
const root = document.querySelector('[data-payment-customer-picker]');
|
|
if (!root) return;
|
|
const search = root.querySelector('input[type=search]');
|
|
const value = root.querySelector('#id_customer');
|
|
const results = root.querySelector('[data-customer-results]');
|
|
const selected = root.querySelector('[data-selected-customer]');
|
|
const status = root.querySelector('[role=status]');
|
|
let timer, controller, version = 0;
|
|
const node = (tag, text, className) => {
|
|
const element = document.createElement(tag);
|
|
if (text !== undefined) element.textContent = text;
|
|
if (className) element.className = className;
|
|
return element;
|
|
};
|
|
const history = customer => {
|
|
const details = node('details', undefined, 'pay-customer-history');
|
|
details.append(node('summary', `سوابق قابل مشاهده · ${customer.ticket_count.toLocaleString('fa-IR')} تیکت`));
|
|
if (!customer.history.length) details.append(node('p', 'سابقه تیکت قابل مشاهدهای وجود ندارد.'));
|
|
customer.history.forEach(ticket => {
|
|
const link = node('a', `${ticket.number} · ${ticket.subject}`);
|
|
link.href = ticket.url; link.target = '_blank'; link.rel = 'noopener';
|
|
const row = node('div'); row.append(link, node('small', ticket.status)); details.append(row);
|
|
});
|
|
return details;
|
|
};
|
|
const identity = customer => {
|
|
const container = node('div', undefined, 'pay-customer-identity');
|
|
container.append(node('strong', customer.name), node('bdi', customer.mobile));
|
|
if (customer.organization) container.append(node('span', customer.organization));
|
|
container.append(node('small', customer.location || 'موقعیت ثبت نشده'));
|
|
return container;
|
|
};
|
|
const choose = (customer, focus = true) => {
|
|
clearTimeout(timer); version++; controller?.abort();
|
|
value.value = customer.value; value.dispatchEvent(new Event('change', {bubbles:true}));
|
|
search.value = ''; results.hidden = true; results.replaceChildren();
|
|
selected.replaceChildren(node('span', 'مشتری انتخابشده', 'pay-selected-label'), identity(customer), history(customer));
|
|
const change = node('button', 'تغییر مشتری', 'pay-button secondary'); change.type = 'button';
|
|
change.addEventListener('click', () => {
|
|
value.value = ''; value.dispatchEvent(new Event('change', {bubbles:true})); selected.hidden = true; search.focus();
|
|
status.textContent = 'مشتری جدید را جستوجو و انتخاب کنید.';
|
|
});
|
|
selected.append(change); selected.hidden = false;
|
|
status.textContent = 'قبل از صدور، نام و شماره تماس مشتری انتخابشده را بررسی کنید.';
|
|
if (focus) change.focus({preventScroll:true});
|
|
};
|
|
const lookup = async () => {
|
|
const query = search.value.trim(), current = ++version;
|
|
controller?.abort(); controller = new AbortController();
|
|
if (query.length < 2) { results.hidden = true; status.textContent = 'حداقل دو حرف یا رقم وارد کنید.'; return; }
|
|
status.textContent = 'در حال جستوجو…';
|
|
try {
|
|
const response = await fetch(`${root.dataset.searchUrl}?q=${encodeURIComponent(query)}`, {
|
|
credentials: 'same-origin', cache: 'no-store', signal: controller.signal
|
|
});
|
|
if (!response.ok) throw new Error('lookup');
|
|
const data = await response.json();
|
|
if (current !== version) return;
|
|
results.replaceChildren();
|
|
data.results.forEach(customer => {
|
|
const card = node('article', undefined, 'pay-customer-result');
|
|
const button = node('button', 'انتخاب این مشتری', 'pay-button secondary'); button.type = 'button';
|
|
button.addEventListener('click', () => choose(customer));
|
|
card.append(identity(customer), history(customer), button); results.append(card);
|
|
});
|
|
results.hidden = !data.results.length;
|
|
status.textContent = data.results.length ? `${data.results.length.toLocaleString('fa-IR')} نتیجه${data.has_more ? '؛ برای نتایج دقیقتر نام یا شماره کاملتری وارد کنید.' : '؛ مشتری موردنظر را انتخاب کنید.'}` : 'مشتری فعالی پیدا نشد؛ نام یا شماره تماس را بررسی کنید.';
|
|
} catch (error) {
|
|
if (error.name === 'AbortError' || current !== version) return;
|
|
results.hidden = true;
|
|
status.textContent = 'جستوجو انجام نشد؛ اتصال را بررسی کنید و دوباره جستوجو کنید.';
|
|
}
|
|
};
|
|
search.addEventListener('input', () => {
|
|
clearTimeout(timer); version++; controller?.abort();
|
|
value.value = ''; value.dispatchEvent(new Event('change', {bubbles:true})); selected.hidden = true; results.hidden = true;
|
|
if (search.value.trim().length < 2) { status.textContent = 'حداقل دو حرف یا رقم وارد کنید.'; return; }
|
|
timer = setTimeout(lookup, 350);
|
|
});
|
|
search.addEventListener('keydown', event => {
|
|
if (event.key === 'Enter') { event.preventDefault(); clearTimeout(timer); lookup(); }
|
|
});
|
|
root.closest('form').addEventListener('submit', event => {
|
|
if (!value.value) {
|
|
event.preventDefault(); status.textContent = 'مشتری را از نتایج جستوجو انتخاب کنید.';
|
|
search.focus(); search.scrollIntoView({block:'center'});
|
|
}
|
|
});
|
|
const initial = JSON.parse(document.querySelector('#payment-selected-customer').textContent);
|
|
if (initial && String(initial.value) === value.value) choose(initial, false);
|
|
})();
|