/** * Dynamic domain select for the security-check create wizard. * * Listens to the core [data-app-lookup] customer picker and loads the customer's * domains from the Security BC gateway. Falls back to manual entry when the * customer has no domains. */ import { getJson } from '/js/core/app-http.js'; import { resolveHost } from '/js/core/app-dom.js'; const SELECTOR = '#security-domain-group[data-app-component="security-customer-domain-select"]'; const EMPTY_API = Object.freeze({ destroy: () => {} }); const resolveGroup = (root) => { const host = resolveHost(root); if (host instanceof HTMLElement && host.matches(SELECTOR)) { return host; } return host.querySelector(SELECTOR); }; export function initSecurityCustomerDomainSelect(root = document) { const group = resolveGroup(root); if (!group) { return EMPTY_API; } const scope = group.closest('form') || document; const lookupContainer = scope.querySelector('[data-app-lookup]'); const domainSelect = group.querySelector('#security-domain'); const domainNoInput = group.querySelector('#security-domain-no'); const domainUrlInput = group.querySelector('#security-domain-url'); const feedback = group.querySelector('#security-domain-feedback'); const manualBlock = group.querySelector('#security-domain-manual'); const manualNoInput = group.querySelector('#security-domain-manual-no'); const manualUrlInput = group.querySelector('#security-domain-manual-url'); if (!lookupContainer || !domainSelect) { return EMPTY_API; } const domainsUrl = group.dataset.domainsUrl || ''; const textInitial = group.dataset.textInitial || ''; const textLoading = group.dataset.textLoading || ''; const textSelect = group.dataset.textSelect || ''; const textError = group.dataset.textError || ''; const listenerController = new AbortController(); const requestController = new AbortController(); const on = (target, type, handler, options = {}) => { if (!target || typeof target.addEventListener !== 'function') { return; } target.addEventListener(type, handler, { ...options, signal: listenerController.signal }); }; const clearOptions = () => { while (domainSelect.options.length > 0) { domainSelect.remove(0); } }; const addPlaceholder = (text) => { const opt = document.createElement('option'); opt.value = ''; opt.textContent = text; domainSelect.appendChild(opt); }; const setFeedback = (text, variant) => { if (!feedback) { return; } feedback.textContent = text; feedback.hidden = !text; feedback.dataset.variant = variant || ''; }; const showManual = (show) => { if (!manualBlock) { return; } manualBlock.hidden = !show; domainSelect.closest('.app-wizard-domain-select-wrap')?.toggleAttribute('hidden', show); if (!show && manualNoInput) manualNoInput.value = ''; if (!show && manualUrlInput) manualUrlInput.value = ''; if (show) { if (domainNoInput) domainNoInput.value = ''; if (domainUrlInput) domainUrlInput.value = ''; } }; const resetDomain = () => { clearOptions(); addPlaceholder(textInitial); domainSelect.disabled = true; if (domainNoInput) domainNoInput.value = ''; if (domainUrlInput) domainUrlInput.value = ''; setFeedback('', ''); showManual(false); }; const loadDomains = async (debitorNo) => { clearOptions(); addPlaceholder(textLoading); domainSelect.disabled = true; group.setAttribute('aria-busy', 'true'); setFeedback('', ''); try { const data = await getJson(`${domainsUrl}?debitor_no=${encodeURIComponent(debitorNo)}`, { signal: requestController.signal, }); const items = Array.isArray(data) ? data : []; clearOptions(); addPlaceholder(textSelect); if (items.length === 0) { domainSelect.disabled = true; showManual(true); return; } showManual(false); items.forEach((item) => { const opt = document.createElement('option'); opt.value = item.value || ''; opt.textContent = item.label || item.value || ''; opt.dataset.url = item.url || ''; domainSelect.appendChild(opt); }); domainSelect.disabled = false; } catch (error) { if (!(error instanceof Error && error.name === 'AbortError')) { clearOptions(); addPlaceholder(textSelect); domainSelect.disabled = true; setFeedback(textError, 'error'); } } finally { group.removeAttribute('aria-busy'); } }; on(domainSelect, 'change', () => { const selected = domainSelect.options[domainSelect.selectedIndex]; if (domainNoInput) domainNoInput.value = selected?.value || ''; if (domainUrlInput) domainUrlInput.value = selected?.dataset.url || ''; }); if (manualNoInput) { on(manualNoInput, 'input', () => { if (domainNoInput) domainNoInput.value = manualNoInput.value.trim(); }); } if (manualUrlInput) { on(manualUrlInput, 'input', () => { if (domainUrlInput) domainUrlInput.value = manualUrlInput.value.trim(); }); } on(lookupContainer, 'lookup:select', (event) => { const debitorNo = event.detail?.value || ''; if (debitorNo) { void loadDomains(debitorNo); } else { resetDomain(); } }); on(lookupContainer, 'lookup:clear', () => { resetDomain(); }); const initialDebitor = group.dataset.initialDebitor || ''; const initialDomain = group.dataset.initialDomain || ''; if (initialDebitor) { void loadDomains(initialDebitor).then(() => { if (initialDomain && !domainSelect.disabled) { domainSelect.value = initialDomain; domainSelect.dispatchEvent(new Event('change')); } }); } else if (domainNoInput && domainNoInput.value && manualNoInput) { manualNoInput.value = domainNoInput.value; } return { destroy: () => { listenerController.abort(); requestController.abort(); }, }; }