(a) public/O-CHAT.html nach BEISPIELE/ verschoben, Test 93 hält den Webroot sauber. (b) bank/fints.php in Teil-Views + public/static/js/fints.js aufgeteilt. (c) settings/bankimport.php in Teil-Views + public/static/js/bankimport.js aufgeteilt. (d) src/Request.php angelegt, BankController als erster Controller umgestellt.
568 lines
26 KiB
JavaScript
568 lines
26 KiB
JavaScript
'use strict';
|
|
|
|
(function () {
|
|
const connections = JSON.parse(document.getElementById('fints-data').dataset.connections);
|
|
const csrfToken = (document.querySelector('meta[name="csrf-token"]') || {}).content || '';
|
|
|
|
// ── NEU-Modal ────────────────────────────────────────────────
|
|
const modalNew = document.getElementById('modal-new-connection');
|
|
const btnNew = document.getElementById('btn-new-connection');
|
|
const btnClose = document.getElementById('btn-modal-close');
|
|
const btnCancel = document.getElementById('btn-modal-cancel');
|
|
const selectNew = document.getElementById('fc-provider');
|
|
const apiNew = document.getElementById('api-fields');
|
|
const fintsNew = document.getElementById('fints-fields');
|
|
const csvNew = document.getElementById('csv-fields');
|
|
const newBankWrap = document.getElementById('fc-new-bank-wrap');
|
|
const newBankInput = document.getElementById('fc-new-bank-name');
|
|
|
|
function openNew() { modalNew.style.display = 'flex'; }
|
|
function closeNew() { modalNew.style.display = 'none'; }
|
|
|
|
if (btnNew) btnNew.addEventListener('click', openNew);
|
|
if (btnClose) btnClose.addEventListener('click', closeNew);
|
|
if (btnCancel) btnCancel.addEventListener('click', closeNew);
|
|
modalNew.addEventListener('click', (e) => { if (e.target === modalNew) closeNew(); });
|
|
|
|
// ── Bankleitzahlen-Verzeichnis: Suche + Formular vorbefüllen (Fix #187) ──
|
|
(function () {
|
|
const blzField = document.getElementById('fc-blz');
|
|
const urlField = document.getElementById('fc-fints-url');
|
|
const searchInput = document.getElementById('fc-blz-search');
|
|
const resultsBox = document.getElementById('fc-blz-results');
|
|
if (!searchInput || !resultsBox) return;
|
|
|
|
function esc(str) {
|
|
return String(str ?? '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
let debounceTimer = null;
|
|
searchInput.addEventListener('input', () => {
|
|
clearTimeout(debounceTimer);
|
|
const q = searchInput.value.trim();
|
|
if (q.length < 2) { resultsBox.style.display = 'none'; resultsBox.innerHTML = ''; return; }
|
|
debounceTimer = setTimeout(async () => {
|
|
let rows;
|
|
try {
|
|
const res = await fetch('/bankinstitute/search?q=' + encodeURIComponent(q));
|
|
rows = await res.json();
|
|
} catch (_) { rows = []; }
|
|
|
|
if (!Array.isArray(rows) || rows.length === 0) {
|
|
resultsBox.innerHTML = '<div style="padding:.4rem .6rem;font-size:.8rem;color:var(--color-muted)">Keine Treffer</div>';
|
|
resultsBox.style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
resultsBox.innerHTML = rows.map((r, i) => `
|
|
<div class="fc-blz-result" data-idx="${i}" style="padding:.35rem .6rem;font-size:.8rem;cursor:pointer;border-bottom:1px solid var(--color-border)">
|
|
<strong>${esc(r.blz)}</strong> ${esc(r.name)} · ${esc(r.ort)}
|
|
${r.pin_tan_url ? '' : '<span style="color:var(--color-muted)"> (kein PIN/TAN-Zugang)</span>'}
|
|
</div>
|
|
`).join('');
|
|
resultsBox.style.display = 'block';
|
|
|
|
resultsBox.querySelectorAll('.fc-blz-result').forEach((el) => {
|
|
el.addEventListener('click', () => {
|
|
const row = rows[parseInt(el.dataset.idx, 10)];
|
|
blzField.value = row.blz;
|
|
urlField.value = row.pin_tan_url || '';
|
|
resultsBox.style.display = 'none';
|
|
searchInput.value = '';
|
|
});
|
|
});
|
|
}, 300);
|
|
});
|
|
})();
|
|
|
|
function syncTypFields(typ) {
|
|
apiNew.style.display = (typ === 'API') ? 'block' : 'none';
|
|
fintsNew.style.display = (typ === 'FinTS') ? 'block' : 'none';
|
|
}
|
|
|
|
selectNew.addEventListener('change', () => {
|
|
const opt = selectNew.options[selectNew.selectedIndex];
|
|
const isNew = opt.value === '__new__';
|
|
const isCsv = opt.value === '__csv__';
|
|
newBankWrap.style.display = isNew ? 'block' : 'none';
|
|
newBankInput.required = isNew;
|
|
csvNew.style.display = isCsv ? 'block' : 'none';
|
|
if (isNew) {
|
|
const selected = modalNew.querySelector('input[name="new_bank_typ"]:checked');
|
|
syncTypFields(selected ? selected.value : 'FinTS');
|
|
} else if (isCsv) {
|
|
syncTypFields('');
|
|
} else {
|
|
syncTypFields(opt.dataset.typ);
|
|
}
|
|
});
|
|
|
|
modalNew.querySelectorAll('input[name="new_bank_typ"]').forEach(radio => {
|
|
radio.addEventListener('change', () => {
|
|
if (selectNew.value === '__new__') {
|
|
syncTypFields(radio.value);
|
|
}
|
|
});
|
|
});
|
|
|
|
// ── Bearbeiten-Modal ─────────────────────────────────────────
|
|
const modalEdit = document.getElementById('modal-edit-connection');
|
|
const btnEditClose = document.getElementById('btn-edit-close');
|
|
const btnEditCancel= document.getElementById('btn-edit-cancel');
|
|
const formEdit = document.getElementById('form-edit-connection');
|
|
const formDelete = document.getElementById('form-edit-delete');
|
|
const btnEditDelete = document.getElementById('btn-edit-delete');
|
|
const editName = document.getElementById('edit-provider-name');
|
|
const editBez = document.getElementById('edit-bezeichnung');
|
|
const editApiFields = document.getElementById('edit-api-fields');
|
|
const editFintsFields = document.getElementById('edit-fints-fields');
|
|
const editAppName = document.getElementById('edit-app-name');
|
|
const editClientId = document.getElementById('edit-client-id');
|
|
const editSecret = document.getElementById('edit-secret');
|
|
const editSandbox = document.getElementById('edit-sandbox');
|
|
const editKuerzel = document.getElementById('edit-kuerzel');
|
|
const editFarbe = document.getElementById('edit-farbe');
|
|
const editBlz = document.getElementById('edit-blz');
|
|
const editBenutzer = document.getElementById('edit-benutzerkennung');
|
|
const editPin = document.getElementById('edit-pin');
|
|
const editFintsUrl = document.getElementById('edit-fints-url');
|
|
const editTanModeId = document.getElementById('edit-tan-mode-id');
|
|
const editAccountList = document.getElementById('edit-account-list');
|
|
const editResetAcc = document.getElementById('edit-reset-accounts');
|
|
const editFetchRangeApi = document.getElementById('edit-fetch-range-api');
|
|
const editFetchRangeFints= document.getElementById('edit-fetch-range-fints');
|
|
|
|
function closeEdit() { modalEdit.style.display = 'none'; }
|
|
|
|
if (btnEditClose) btnEditClose.addEventListener('click', closeEdit);
|
|
if (btnEditCancel) btnEditCancel.addEventListener('click', closeEdit);
|
|
modalEdit.addEventListener('click', (e) => { if (e.target === modalEdit) closeEdit(); });
|
|
|
|
btnEditDelete.addEventListener('click', () => {
|
|
if (!confirm('Verbindung wirklich löschen?')) return;
|
|
formDelete.submit();
|
|
});
|
|
|
|
// ── Abruf-Modal ──────────────────────────────────────────────
|
|
const modalRun = document.getElementById('modal-run-log');
|
|
const runLogArea = document.getElementById('run-log-area');
|
|
const runTitle = document.getElementById('run-log-title');
|
|
const runSpinner = document.getElementById('run-log-spinner');
|
|
const btnRunClose= document.getElementById('btn-run-close');
|
|
let activeEs = null;
|
|
let runWasOk = false;
|
|
|
|
function closeRun() {
|
|
if (activeEs) { activeEs.close(); activeEs = null; }
|
|
if (runWasOk) {
|
|
window.location.href = '/bank';
|
|
return;
|
|
}
|
|
modalRun.style.display = 'none';
|
|
runLogArea.textContent = '';
|
|
}
|
|
|
|
btnRunClose.addEventListener('click', closeRun);
|
|
modalRun.addEventListener('click', (e) => { if (e.target === modalRun && !btnRunClose.disabled) closeRun(); });
|
|
|
|
// ── Konto-Auswahl-Modal ──────────────────────────────────────
|
|
const modalAcc = document.getElementById('modal-account-select');
|
|
const accList = document.getElementById('account-list');
|
|
const accError = document.getElementById('account-error');
|
|
const btnAccSubmit = document.getElementById('btn-account-submit');
|
|
const btnAccCancel = document.getElementById('btn-account-cancel');
|
|
let currentAccToken = null;
|
|
|
|
function openAccountModal(payload) {
|
|
currentAccToken = payload.token;
|
|
accList.innerHTML = '';
|
|
accError.style.display = 'none';
|
|
payload.accounts.forEach((acc) => {
|
|
const row = document.createElement('label');
|
|
row.style.display = 'flex';
|
|
row.style.alignItems = 'center';
|
|
row.style.gap = '.5rem';
|
|
row.style.cursor = 'pointer';
|
|
row.style.padding = '.35rem .5rem';
|
|
row.style.border = '1px solid #e2e8f0';
|
|
row.style.borderRadius = 'var(--radius)';
|
|
const cb = document.createElement('input');
|
|
cb.type = 'checkbox'; cb.value = acc.id; cb.checked = true;
|
|
cb.style.margin = '0';
|
|
const label = document.createElement('span');
|
|
label.textContent = acc.label;
|
|
row.appendChild(cb);
|
|
row.appendChild(label);
|
|
accList.appendChild(row);
|
|
});
|
|
modalAcc.style.display = 'flex';
|
|
}
|
|
function closeAccountModal() {
|
|
modalAcc.style.display = 'none';
|
|
currentAccToken = null;
|
|
}
|
|
|
|
btnAccCancel.addEventListener('click', () => {
|
|
closeAccountModal();
|
|
if (activeEs) { activeEs.close(); activeEs = null; }
|
|
runTitle.textContent = 'Abruf abgebrochen';
|
|
runSpinner.textContent = '✗ abgebrochen';
|
|
btnRunClose.disabled = false;
|
|
});
|
|
|
|
btnAccSubmit.addEventListener('click', async () => {
|
|
const ids = Array.from(accList.querySelectorAll('input[type=checkbox]:checked')).map(cb => cb.value);
|
|
if (ids.length === 0) {
|
|
accError.textContent = 'Mindestens ein Konto auswählen.';
|
|
accError.style.display = 'block';
|
|
return;
|
|
}
|
|
btnAccSubmit.disabled = true;
|
|
try {
|
|
const form = new FormData();
|
|
form.append('token', currentAccToken);
|
|
form.append('ids', JSON.stringify(ids));
|
|
const res = await fetch('/bank/direkt/' + currentSourceId + '/accounts', { method: 'POST', body: form });
|
|
const data = await res.json();
|
|
if (!res.ok || data.error) {
|
|
accError.textContent = data.error || 'Übermittlung fehlgeschlagen.';
|
|
accError.style.display = 'block';
|
|
return;
|
|
}
|
|
closeAccountModal();
|
|
} catch (e) {
|
|
accError.textContent = 'Netzwerkfehler: ' + e.message;
|
|
accError.style.display = 'block';
|
|
} finally {
|
|
btnAccSubmit.disabled = false;
|
|
}
|
|
});
|
|
|
|
// ── TAN-Modal ────────────────────────────────────────────────
|
|
const modalTan = document.getElementById('modal-tan-input');
|
|
const tanContext = document.getElementById('tan-context');
|
|
const tanChallenge = document.getElementById('tan-challenge');
|
|
const tanImageWrap = document.getElementById('tan-image-wrap');
|
|
const tanMedium = document.getElementById('tan-medium');
|
|
const tanInput = document.getElementById('tan-input');
|
|
const tanError = document.getElementById('tan-error');
|
|
const btnTanSubmit = document.getElementById('btn-tan-submit');
|
|
const btnTanCancel = document.getElementById('btn-tan-cancel');
|
|
let currentSourceId = null;
|
|
let currentTanToken = null;
|
|
|
|
function openTanModal(payload) {
|
|
currentTanToken = payload.token;
|
|
tanContext.textContent = payload.context ? 'Konto: ' + payload.context : '';
|
|
tanChallenge.textContent = payload.challenge || '';
|
|
tanMedium.textContent = payload.mediumName ? 'TAN-Medium: ' + payload.mediumName : '';
|
|
tanImageWrap.innerHTML = '';
|
|
if (payload.imageData && payload.imageMime) {
|
|
const img = new Image();
|
|
img.src = 'data:' + payload.imageMime + ';base64,' + payload.imageData;
|
|
img.style.maxWidth = '320px';
|
|
tanImageWrap.appendChild(img);
|
|
}
|
|
tanInput.value = '';
|
|
tanError.style.display = 'none';
|
|
modalTan.style.display = 'flex';
|
|
setTimeout(() => tanInput.focus(), 50);
|
|
}
|
|
function closeTanModal() {
|
|
modalTan.style.display = 'none';
|
|
currentTanToken = null;
|
|
}
|
|
|
|
btnTanCancel.addEventListener('click', () => {
|
|
closeTanModal();
|
|
if (activeEs) { activeEs.close(); activeEs = null; }
|
|
runTitle.textContent = 'Abruf abgebrochen';
|
|
runSpinner.textContent = '✗ abgebrochen';
|
|
btnRunClose.disabled = false;
|
|
});
|
|
|
|
async function submitTan() {
|
|
const tan = tanInput.value.trim();
|
|
if (!tan) { tanError.textContent = 'Bitte TAN eingeben.'; tanError.style.display = 'block'; return; }
|
|
if (!currentSourceId || !currentTanToken) return;
|
|
btnTanSubmit.disabled = true;
|
|
try {
|
|
const form = new FormData();
|
|
form.append('token', currentTanToken);
|
|
form.append('tan', tan);
|
|
const res = await fetch('/bank/direkt/' + currentSourceId + '/tan', { method: 'POST', body: form });
|
|
const data = await res.json();
|
|
if (!res.ok || data.error) {
|
|
tanError.textContent = data.error || 'TAN-Übermittlung fehlgeschlagen.';
|
|
tanError.style.display = 'block';
|
|
return;
|
|
}
|
|
closeTanModal();
|
|
} catch (e) {
|
|
tanError.textContent = 'Netzwerkfehler: ' + e.message;
|
|
tanError.style.display = 'block';
|
|
} finally {
|
|
btnTanSubmit.disabled = false;
|
|
}
|
|
}
|
|
btnTanSubmit.addEventListener('click', submitTan);
|
|
tanInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') submitTan(); });
|
|
|
|
// ── Drag & Drop für Kachel-Reihenfolge ──────────────────────
|
|
const grid = document.getElementById('connection-grid');
|
|
let dragSrc = null;
|
|
let didDrag = false;
|
|
if (grid) {
|
|
grid.querySelectorAll('.drag-handle').forEach(handle => {
|
|
handle.addEventListener('dragstart', (e) => {
|
|
dragSrc = handle.closest('.card');
|
|
didDrag = false;
|
|
if (!dragSrc) return;
|
|
e.dataTransfer.effectAllowed = 'move';
|
|
e.dataTransfer.setData('text/plain', dragSrc.dataset.id || '');
|
|
// Komplette Kachel als Drag-Image, am Mauszeiger an der Klick-Position
|
|
const rect = dragSrc.getBoundingClientRect();
|
|
e.dataTransfer.setDragImage(dragSrc, e.clientX - rect.left, e.clientY - rect.top);
|
|
// Ursprung subtil markieren, erst nach Snapshot (sonst landet das Highlight im Ghost)
|
|
setTimeout(() => {
|
|
if (dragSrc) {
|
|
dragSrc.style.outline = '2px dashed #6366f1';
|
|
dragSrc.style.outlineOffset = '2px';
|
|
}
|
|
}, 0);
|
|
});
|
|
handle.addEventListener('dragend', () => {
|
|
if (dragSrc) {
|
|
dragSrc.style.outline = '';
|
|
dragSrc.style.outlineOffset = '';
|
|
}
|
|
grid.querySelectorAll('.drop-before, .drop-after').forEach(c => {
|
|
c.classList.remove('drop-before', 'drop-after');
|
|
});
|
|
dragSrc = null;
|
|
setTimeout(() => { didDrag = false; }, 50);
|
|
});
|
|
});
|
|
function clearDropMarkers() {
|
|
grid.querySelectorAll('.drop-before, .drop-after').forEach(c => {
|
|
c.classList.remove('drop-before', 'drop-after');
|
|
});
|
|
}
|
|
|
|
function findDropTarget(clientX, clientY) {
|
|
const cards = [...grid.querySelectorAll('.card')].filter(c => c !== dragSrc);
|
|
// Erst: Karte unter dem Cursor (bevorzugt)
|
|
for (const card of cards) {
|
|
const r = card.getBoundingClientRect();
|
|
if (clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom) {
|
|
return { card, side: (clientX < r.left + r.width / 2) ? 'before' : 'after' };
|
|
}
|
|
}
|
|
// Fallback: nächste Karte in derselben Zeile (anhand Y)
|
|
let best = null, bestDist = Infinity;
|
|
for (const card of cards) {
|
|
const r = card.getBoundingClientRect();
|
|
if (clientY >= r.top && clientY <= r.bottom) {
|
|
const center = r.left + r.width / 2;
|
|
const dist = Math.abs(clientX - center);
|
|
if (dist < bestDist) {
|
|
bestDist = dist;
|
|
best = { card, side: (clientX < center) ? 'before' : 'after' };
|
|
}
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
grid.addEventListener('dragover', (e) => {
|
|
if (!dragSrc) return;
|
|
e.preventDefault();
|
|
e.dataTransfer.dropEffect = 'move';
|
|
const target = findDropTarget(e.clientX, e.clientY);
|
|
if (!target) { clearDropMarkers(); return; }
|
|
const cls = target.side === 'before' ? 'drop-before' : 'drop-after';
|
|
// Nur neu setzen wenn nötig (vermeidet Flackern)
|
|
if (target.card.classList.contains(cls)) return;
|
|
clearDropMarkers();
|
|
target.card.classList.add(cls);
|
|
});
|
|
grid.addEventListener('dragleave', (e) => {
|
|
// Nur wenn der Grid komplett verlassen wird
|
|
if (e.target === grid && !grid.contains(e.relatedTarget)) {
|
|
clearDropMarkers();
|
|
}
|
|
});
|
|
grid.addEventListener('drop', (e) => {
|
|
if (!dragSrc) return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const marker = grid.querySelector('.drop-before, .drop-after');
|
|
if (marker) {
|
|
const before = marker.classList.contains('drop-before');
|
|
marker.parentNode.insertBefore(dragSrc, before ? marker : marker.nextSibling);
|
|
didDrag = true;
|
|
persistOrder();
|
|
}
|
|
clearDropMarkers();
|
|
});
|
|
}
|
|
async function persistOrder() {
|
|
const ids = Array.from(grid.querySelectorAll('.card[data-id]')).map(c => c.dataset.id);
|
|
const form = new FormData();
|
|
form.append('ids', JSON.stringify(ids));
|
|
try {
|
|
await fetch('/bank/direkt/_order', { method: 'POST', body: form });
|
|
} catch (e) { /* still local */ }
|
|
}
|
|
|
|
document.querySelectorAll('.btn-run-connection').forEach(card => {
|
|
card.addEventListener('click', (e) => {
|
|
if (didDrag) { didDrag = false; return; }
|
|
if (e.target.closest('.btn-edit-connection')) return;
|
|
if (e.target.closest('.drag-handle')) return;
|
|
|
|
const id = card.dataset.id;
|
|
currentSourceId = id;
|
|
runLogArea.textContent = '';
|
|
runTitle.textContent = 'Abruf läuft…';
|
|
runSpinner.textContent = '⏳ wird abgerufen…';
|
|
btnRunClose.disabled = true;
|
|
runWasOk = false;
|
|
modalRun.style.display = 'flex';
|
|
|
|
if (activeEs) activeEs.close();
|
|
activeEs = new EventSource('/bank/direkt/' + id + '/run?csrf_token=' + encodeURIComponent(csrfToken));
|
|
|
|
activeEs.addEventListener('log', (ev) => {
|
|
runLogArea.textContent += ev.data + '\n';
|
|
runLogArea.scrollTop = runLogArea.scrollHeight;
|
|
});
|
|
|
|
activeEs.addEventListener('tan-required', (ev) => {
|
|
try {
|
|
openTanModal(JSON.parse(ev.data));
|
|
} catch (e) {
|
|
runLogArea.textContent += '\n[TAN-Anforderung konnte nicht geparst werden]\n';
|
|
}
|
|
});
|
|
|
|
activeEs.addEventListener('accounts-required', (ev) => {
|
|
try {
|
|
openAccountModal(JSON.parse(ev.data));
|
|
} catch (e) {
|
|
runLogArea.textContent += '\n[Konto-Auswahl konnte nicht geparst werden]\n';
|
|
}
|
|
});
|
|
|
|
activeEs.addEventListener('done', (ev) => {
|
|
activeEs.close();
|
|
activeEs = null;
|
|
runWasOk = ev.data === 'ok';
|
|
runTitle.textContent = runWasOk ? 'Abruf abgeschlossen' : 'Abruf mit Fehlern beendet';
|
|
runSpinner.textContent = runWasOk ? '✓ fertig' : '⚠ Fehler';
|
|
btnRunClose.disabled = false;
|
|
});
|
|
|
|
activeEs.addEventListener('error', () => {
|
|
activeEs.close();
|
|
activeEs = null;
|
|
runLogArea.textContent += '\n[Verbindung unterbrochen]\n';
|
|
runLogArea.scrollTop = runLogArea.scrollHeight;
|
|
runTitle.textContent = 'Abruf unterbrochen';
|
|
runSpinner.textContent = '✗ unterbrochen';
|
|
btnRunClose.disabled = false;
|
|
});
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.btn-edit-connection').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const id = parseInt(btn.dataset.id, 10);
|
|
const conn = connections.find(c => c.id === id);
|
|
if (!conn) return;
|
|
|
|
formEdit.action = '/bank/direkt/' + id;
|
|
formDelete.action = '/bank/direkt/' + id + '/delete';
|
|
|
|
editName.textContent = conn.provider_name;
|
|
editBez.value = conn.bezeichnung;
|
|
editKuerzel.value = conn.kuerzel || '';
|
|
editFarbe.value = conn.farbe || '#6366f1';
|
|
|
|
const isApi = conn.type === 'api';
|
|
const isFints = conn.type === 'fints';
|
|
|
|
editApiFields.style.display = isApi ? 'block' : 'none';
|
|
editFintsFields.style.display = isFints ? 'block' : 'none';
|
|
|
|
// fetch_range steht in beiden Type-Blöcken; nur der zum aktiven Typ
|
|
// gehörige Select wird enabled (disabled Felder werden nicht submitted).
|
|
const fetchVal = conn.fetch_range || 'last_fetched';
|
|
editFetchRangeApi.value = fetchVal;
|
|
editFetchRangeFints.value = fetchVal;
|
|
editFetchRangeApi.disabled = !isApi;
|
|
editFetchRangeFints.disabled= !isFints;
|
|
|
|
if (isApi) {
|
|
editAppName.value = conn.app_name || '';
|
|
editClientId.value = '';
|
|
editSecret.value = '';
|
|
editSandbox.checked = conn.sandbox === true;
|
|
}
|
|
if (isFints) {
|
|
editBlz.value = conn.blz || '';
|
|
editBenutzer.value = conn.benutzerkennung || '';
|
|
editPin.value = '';
|
|
editFintsUrl.value = conn.fints_url || '';
|
|
editTanModeId.value = conn.tan_mode_id != null ? conn.tan_mode_id : '';
|
|
editResetAcc.checked = false;
|
|
|
|
// Konto-Checkboxen aus account_filter aufbauen
|
|
editAccountList.innerHTML = '';
|
|
let accounts = [];
|
|
if (conn.account_filter) {
|
|
try {
|
|
const arr = JSON.parse(conn.account_filter);
|
|
if (Array.isArray(arr)) accounts = arr.map(String);
|
|
} catch (e) { /* ignore */ }
|
|
}
|
|
const editAccountEmpty = document.getElementById('edit-account-empty');
|
|
if (accounts.length === 0) {
|
|
editAccountList.style.display = 'none';
|
|
editAccountEmpty.style.display = 'block';
|
|
} else {
|
|
editAccountList.style.display = 'flex';
|
|
editAccountEmpty.style.display = 'none';
|
|
accounts.forEach(id => {
|
|
const row = document.createElement('label');
|
|
row.style.display = 'flex';
|
|
row.style.alignItems = 'center';
|
|
row.style.gap = '.5rem';
|
|
row.style.cursor = 'pointer';
|
|
row.style.fontWeight = 'normal';
|
|
row.style.margin = '0';
|
|
const cb = document.createElement('input');
|
|
cb.type = 'checkbox';
|
|
cb.name = 'active_accounts[]';
|
|
cb.value = id;
|
|
cb.checked = true;
|
|
cb.style.width = 'auto';
|
|
cb.style.margin = '0';
|
|
const span = document.createElement('span');
|
|
// IBAN lesbarer machen (4er-Gruppen), AN:-Identifier wie geliefert
|
|
span.textContent = /^[A-Z]{2}\d{2}/.test(id)
|
|
? id.replace(/(.{4})/g, '$1 ').trim()
|
|
: id;
|
|
span.style.fontFamily = 'monospace';
|
|
span.style.fontSize = '.85rem';
|
|
row.appendChild(cb);
|
|
row.appendChild(span);
|
|
editAccountList.appendChild(row);
|
|
});
|
|
}
|
|
}
|
|
|
|
modalEdit.style.display = 'flex';
|
|
});
|
|
});
|
|
})();
|