'use strict'; (function () { const DATE_FORMAT_OPTIONS = JSON.parse(document.getElementById('bankimport-data').dataset.dateFormatOptions); function badgeTextColor(hex) { const r = parseInt(hex.slice(1,3),16); const g = parseInt(hex.slice(3,5),16); const b = parseInt(hex.slice(5,7),16); return (0.299*r + 0.587*g + 0.114*b) > 128 ? '#1e293b' : '#ffffff'; } function updatePreview(colorInput, hex) { const preview = colorInput.closest('div')?.querySelector('.bi-kuerzel-preview'); if (!preview) return; preview.style.background = hex; preview.style.color = badgeTextColor(hex); } // ── Hilfsfunktionen ────────────────────────────────────────────── function getFormatId(cell) { return cell.closest('[data-format-id]')?.dataset.formatId; } function showError(cell, msg) { cell.classList.add('bi-cell--error'); let err = cell.parentElement.querySelector('.bi-error-msg'); if (!err) { err = document.createElement('span'); err.className = 'bi-error-msg'; cell.after(err); } err.textContent = msg; setTimeout(() => { cell.classList.remove('bi-cell--error'); err.remove(); }, 3000); } async function saveField(formatId, field, value) { const body = new URLSearchParams({ field, value: value ?? '' }); const res = await fetch(`/settings/bankimport/${formatId}/field`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString(), }); return res.json(); } function displayForCell(type, value, field) { if (type === 'number' || type === 'nullint') { if (value === '' || value === null || value === undefined) { return '–'; } return `${value}`; } if (type === 'char' || type === 'char0') { if (value === '') return 'keines'; return `${value.replace(/&/g,'&').replace(//g,'>')}`; } if (type === 'dateformat') { const example = DATE_FORMAT_OPTIONS[value] || '?'; return `${value}→ ${example}`; } if (type === 'template') { if (!value) return 'nicht gesetzt'; const esc = value.replace(/&/g,'&').replace(//g,'>'); return `${esc}`; } if (type === 'text') { return value.replace(/&/g,'&').replace(//g,'>'); } return value; } // ── Klick-Handler für .bi-cell ─────────────────────────────────── document.addEventListener('click', (e) => { const cell = e.target.closest('.bi-cell'); if (!cell || cell.querySelector('input,select')) return; const field = cell.dataset.field; const type = cell.dataset.type; const rawValue = cell.dataset.value ?? ''; const formatId = getFormatId(cell); if (!formatId) return; const originalHTML = cell.innerHTML; cell.innerHTML = ''; cell.classList.remove('bi-cell--error'); let el; if (type === 'number' || type === 'nullint') { el = document.createElement('input'); el.type = 'number'; el.min = cell.dataset.min ?? '0'; el.max = cell.dataset.max ?? '99'; el.value = rawValue; } else if (type === 'char') { el = document.createElement('input'); el.type = 'text'; el.maxLength = 1; el.value = rawValue; } else if (type === 'char0') { el = document.createElement('input'); el.type = 'text'; el.maxLength = 1; el.value = rawValue; el.placeholder = '(leer)'; el.style.width = '70px'; } else if (type === 'dateformat') { el = document.createElement('select'); for (const [fmt, example] of Object.entries(DATE_FORMAT_OPTIONS)) { const opt = document.createElement('option'); opt.value = fmt; opt.textContent = `${fmt} → ${example}`; if (fmt === rawValue) opt.selected = true; el.appendChild(opt); } } else if (type === 'template') { el = document.createElement('input'); el.type = 'text'; el.value = rawValue; el.style.width = '100%'; el.style.boxSizing = 'border-box'; } else { el = document.createElement('input'); el.type = 'text'; el.value = rawValue; el.style.width = '180px'; } cell.appendChild(el); el.focus(); if (el.tagName === 'INPUT' && el.type === 'text') el.select(); let done = false; async function commit() { if (done) return; done = true; const newVal = el.tagName === 'SELECT' ? el.value : el.value; cell.classList.add('bi-cell--saving'); cell.innerHTML = ''; let json; try { json = await saveField(formatId, field, newVal); } catch (_) { json = { error: 'Verbindungsfehler.' }; } cell.classList.remove('bi-cell--saving'); if (json.error) { cell.innerHTML = originalHTML; showError(cell, json.error); return; } cell.dataset.value = newVal; cell.innerHTML = displayForCell(type, newVal, field); // Formatname im Card-Header synchronisieren if (field === 'name') { const title = cell.closest('.card')?.querySelector('.card-title'); if (title) title.dataset.value = newVal; } // Kürzel-Vorschau-Badge aktualisieren if (field === 'kuerzel') { const preview = cell.closest('div')?.querySelector('.bi-kuerzel-preview'); if (preview) preview.textContent = newVal; } } function cancel() { if (done) return; done = true; cell.innerHTML = originalHTML; } if (el.tagName === 'SELECT') { el.addEventListener('change', commit); el.addEventListener('keydown', (ev) => { if (ev.key === 'Escape') { ev.stopPropagation(); cancel(); } }); el.addEventListener('blur', () => { if (!done) commit(); }); } else { el.addEventListener('blur', commit); el.addEventListener('keydown', (ev) => { if (ev.key === 'Enter') { ev.preventDefault(); el.blur(); } if (ev.key === 'Escape') { ev.preventDefault(); cancel(); } }); } }); // ── Colorpicker für farbe ──────────────────────────────────────── document.addEventListener('input', (e) => { const ci = e.target.closest('.bi-color'); if (ci) updatePreview(ci, ci.value); }); document.addEventListener('change', async (e) => { const ci = e.target.closest('.bi-color'); if (!ci) return; const formatId = getFormatId(ci); const field = ci.dataset.field; const value = ci.value; ci.disabled = true; let json; try { json = await saveField(formatId, field, value); } catch (_) { json = { error: 'Verbindungsfehler.' }; } ci.disabled = false; if (json.error) { showError(ci, json.error); } else { updatePreview(ci, value); } }); // ── Format löschen ────────────────────────────────────────────── document.addEventListener('click', async (e) => { const btn = e.target.closest('.bi-delete-format'); if (!btn) return; const formatId = btn.dataset.formatId; const formatName = btn.dataset.formatName; if (!confirm(`Format „${formatName}" wirklich löschen?`)) return; btn.disabled = true; let json; try { const res = await fetch(`/settings/bankimport/${formatId}/delete`, { method: 'POST' }); json = await res.json(); } catch (_) { json = { error: 'Verbindungsfehler.' }; } if (json.error) { alert(json.error); btn.disabled = false; return; } btn.closest('.card').remove(); }); // ── Vorschlags-Card verwerfen ──────────────────────────────────── const dismissBtn = document.getElementById('suggestion-dismiss'); if (dismissBtn) { dismissBtn.addEventListener('click', async () => { dismissBtn.disabled = true; try { await fetch('/settings/bankimport/dismiss-suggestion', { method: 'POST' }); } catch (_) {} const card = document.getElementById('suggestion-card'); if (card) card.remove(); }); } // ── Checkboxen für has_header_row ──────────────────────────────── document.addEventListener('change', async (e) => { const cb = e.target.closest('.bi-checkbox'); if (!cb) return; const label = cb.closest('.bi-checkbox-label'); const textSpan = label?.querySelector('.bi-checkbox-text'); const field = cb.dataset.field; const formatId = getFormatId(cb); if (!formatId) return; const newVal = cb.checked ? '1' : '0'; cb.disabled = true; let json; try { json = await saveField(formatId, field, newVal); } catch (_) { json = { error: 'Verbindungsfehler.' }; } cb.disabled = false; if (json.error) { cb.checked = !cb.checked; // Revert if (textSpan) textSpan.textContent = cb.checked ? 'Ja' : 'Nein'; alert(json.error); return; } if (textSpan) textSpan.textContent = cb.checked ? 'Ja' : 'Nein'; }); })();