diff --git a/partials/landingpages/modules/setup.php b/partials/landingpages/modules/setup.php index 2e058aa..b0b1193 100644 --- a/partials/landingpages/modules/setup.php +++ b/partials/landingpages/modules/setup.php @@ -504,6 +504,11 @@ $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups) $fxCatalogAvailable = $fxCatalogOptions !== []; $fxPreferred = is_array($current['preferred_currencies'] ?? null) ? $current['preferred_currencies'] : []; $fxUseSeparateDb = !empty($current['use_separate_db']); + $fxCatalogJson = json_encode(array_map( + static fn (string $name, string $code): array => ['code' => $code, 'name' => $name], + array_values($fxCatalogOptions), + array_keys($fxCatalogOptions) + ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>
@@ -558,24 +563,36 @@ $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups)
@@ -727,7 +744,232 @@ $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups) toggle.addEventListener('change', sync); sync(); })(); + + (() => { + const baseRoot = document.querySelector('[data-fx-base-picker]'); + const baseHidden = document.querySelector('[data-fx-base-hidden]'); + const baseInput = document.querySelector('[data-fx-base-input]'); + const baseSuggestions = document.querySelector('[data-fx-base-suggestions]'); + + const multiRoot = document.querySelector('[data-fx-multi-picker]'); + const multiInput = document.querySelector('[data-fx-multi-input]'); + const multiSuggestions = document.querySelector('[data-fx-multi-suggestions]'); + const tagsRoot = document.querySelector('[data-fx-tags]'); + + const parseCurrencies = (root) => { + if (!root) return []; + try { + const parsed = JSON.parse(root.dataset.currencies || '[]'); + return Array.isArray(parsed) ? parsed : []; + } catch (error) { + return []; + } + }; + + const currencies = parseCurrencies(baseRoot || multiRoot); + const formatLabel = (item) => `${item.code} - ${item.name}`; + const searchCurrencies = (query, excluded = []) => { + const needle = String(query || '').trim().toLowerCase(); + const excludedSet = new Set(excluded); + const filtered = currencies.filter((item) => { + if (!item || !item.code || excludedSet.has(item.code)) { + return false; + } + if (needle === '') { + return true; + } + return item.code.toLowerCase().includes(needle) || String(item.name || '').toLowerCase().includes(needle); + }); + return filtered.slice(0, 12); + }; + + const closeSuggestions = (node) => { + if (!node) return; + node.hidden = true; + node.innerHTML = ''; + }; + + const renderSuggestions = (node, items, onSelect) => { + if (!node || items.length === 0) { + closeSuggestions(node); + return; + } + node.innerHTML = ''; + items.forEach((item) => { + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'fx-setup-suggestion'; + button.textContent = formatLabel(item); + button.addEventListener('click', () => onSelect(item)); + node.appendChild(button); + }); + node.hidden = false; + }; + + if (baseRoot && baseHidden && baseInput && baseSuggestions) { + const applyBase = (item) => { + baseHidden.value = item.code; + baseInput.value = formatLabel(item); + closeSuggestions(baseSuggestions); + }; + + baseInput.addEventListener('input', () => { + baseHidden.value = ''; + renderSuggestions(baseSuggestions, searchCurrencies(baseInput.value), applyBase); + }); + + baseInput.addEventListener('focus', () => { + renderSuggestions(baseSuggestions, searchCurrencies(baseInput.value), applyBase); + }); + + baseInput.form?.addEventListener('submit', () => { + if (baseHidden.value !== '') { + return; + } + const first = searchCurrencies(baseInput.value)[0] || null; + if (first) { + applyBase(first); + } + }); + } + + if (multiRoot && multiInput && multiSuggestions && tagsRoot) { + const selectedCodes = () => Array.from(tagsRoot.querySelectorAll('[data-code]')).map((node) => node.getAttribute('data-code') || ''); + + const addTag = (item) => { + if (!item || selectedCodes().includes(item.code)) { + return; + } + + const tag = document.createElement('span'); + tag.className = 'fx-setup-tag'; + tag.setAttribute('data-code', item.code); + tag.append(document.createTextNode(item.code)); + + const remove = document.createElement('button'); + remove.type = 'button'; + remove.setAttribute('data-remove-code', item.code); + remove.setAttribute('aria-label', 'Entfernen'); + remove.textContent = 'x'; + remove.addEventListener('click', () => { + tag.remove(); + hidden.remove(); + renderSuggestions(multiSuggestions, searchCurrencies(multiInput.value, selectedCodes()), addTag); + }); + tag.appendChild(remove); + tagsRoot.appendChild(tag); + + const hidden = document.createElement('input'); + hidden.type = 'hidden'; + hidden.name = 'preferred_currencies[]'; + hidden.value = item.code; + hidden.setAttribute('data-code-hidden', item.code); + multiRoot.appendChild(hidden); + + multiInput.value = ''; + renderSuggestions(multiSuggestions, searchCurrencies('', selectedCodes()), addTag); + }; + + tagsRoot.querySelectorAll('[data-remove-code]').forEach((button) => { + button.addEventListener('click', () => { + const code = button.getAttribute('data-remove-code') || ''; + tagsRoot.querySelector(`[data-code="${code}"]`)?.remove(); + multiRoot.querySelector(`[data-code-hidden="${code}"]`)?.remove(); + renderSuggestions(multiSuggestions, searchCurrencies(multiInput.value, selectedCodes()), addTag); + }); + }); + + multiInput.addEventListener('input', () => { + renderSuggestions(multiSuggestions, searchCurrencies(multiInput.value, selectedCodes()), addTag); + }); + + multiInput.addEventListener('focus', () => { + renderSuggestions(multiSuggestions, searchCurrencies(multiInput.value, selectedCodes()), addTag); + }); + } + + document.addEventListener('click', (event) => { + if (baseRoot && !baseRoot.contains(event.target)) { + closeSuggestions(baseSuggestions); + } + if (multiRoot && !multiRoot.contains(event.target)) { + closeSuggestions(multiSuggestions); + } + }); + })(); +