693 lines
23 KiB
JavaScript
693 lines
23 KiB
JavaScript
/**
|
||
* Dateiverwaltung Frontend
|
||
* Zwei getrennte Bereiche: Mail-Abruf und Datei-Sortierung
|
||
*/
|
||
|
||
// ============ API ============
|
||
|
||
async function api(endpoint, options = {}) {
|
||
const response = await fetch(`/api${endpoint}`, {
|
||
headers: { 'Content-Type': 'application/json', ...options.headers },
|
||
...options
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({}));
|
||
throw new Error(error.detail || 'API Fehler');
|
||
}
|
||
return response.json();
|
||
}
|
||
|
||
// ============ Loading Overlay ============
|
||
|
||
function zeigeLoading(text = 'Wird geladen...') {
|
||
document.getElementById('loading-text').textContent = text;
|
||
document.getElementById('loading-overlay').classList.remove('hidden');
|
||
}
|
||
|
||
function versteckeLoading() {
|
||
document.getElementById('loading-overlay').classList.add('hidden');
|
||
}
|
||
|
||
// ============ File Browser ============
|
||
|
||
let browserTargetInput = null;
|
||
let browserCurrentPath = '/srv/http/dateiverwaltung/data';
|
||
|
||
function oeffneBrowser(inputId) {
|
||
browserTargetInput = inputId;
|
||
const currentValue = document.getElementById(inputId).value;
|
||
browserCurrentPath = currentValue || '/srv/http/dateiverwaltung/data';
|
||
ladeBrowserInhalt(browserCurrentPath);
|
||
document.getElementById('browser-modal').classList.remove('hidden');
|
||
}
|
||
|
||
async function ladeBrowserInhalt(path) {
|
||
try {
|
||
const data = await api(`/browse?path=${encodeURIComponent(path)}`);
|
||
|
||
if (data.error) {
|
||
document.getElementById('browser-list').innerHTML =
|
||
`<li class="file-browser-item" style="color: var(--danger);">${data.error}</li>`;
|
||
return;
|
||
}
|
||
|
||
browserCurrentPath = data.current;
|
||
document.getElementById('browser-current-path').textContent = data.current;
|
||
|
||
let html = '';
|
||
|
||
// Parent directory
|
||
if (data.parent) {
|
||
html += `<li class="file-browser-item" onclick="ladeBrowserInhalt('${data.parent}')">
|
||
<span class="file-icon">📁</span> ..
|
||
</li>`;
|
||
}
|
||
|
||
// Directories
|
||
for (const entry of data.entries) {
|
||
html += `<li class="file-browser-item" ondblclick="ladeBrowserInhalt('${entry.path}')" onclick="browserSelect(this, '${entry.path}')">
|
||
<span class="file-icon">📁</span> ${entry.name}
|
||
</li>`;
|
||
}
|
||
|
||
if (data.entries.length === 0 && !data.parent) {
|
||
html = '<li class="file-browser-item">Keine Unterordner</li>';
|
||
}
|
||
|
||
document.getElementById('browser-list').innerHTML = html;
|
||
} catch (error) {
|
||
document.getElementById('browser-list').innerHTML =
|
||
`<li class="file-browser-item" style="color: var(--danger);">Fehler: ${error.message}</li>`;
|
||
}
|
||
}
|
||
|
||
function browserSelect(element, path) {
|
||
document.querySelectorAll('.file-browser-item.selected').forEach(el => el.classList.remove('selected'));
|
||
element.classList.add('selected');
|
||
browserCurrentPath = path;
|
||
}
|
||
|
||
function browserAuswahl() {
|
||
if (browserTargetInput && browserCurrentPath) {
|
||
document.getElementById(browserTargetInput).value = browserCurrentPath + '/';
|
||
}
|
||
schliesseModal('browser-modal');
|
||
}
|
||
|
||
// ============ Checkbox Helpers ============
|
||
|
||
function getCheckedTypes(groupId) {
|
||
const checkboxes = document.querySelectorAll(`#${groupId} input[type="checkbox"]:checked`);
|
||
return Array.from(checkboxes).map(cb => cb.value);
|
||
}
|
||
|
||
function setCheckedTypes(groupId, types) {
|
||
const checkboxes = document.querySelectorAll(`#${groupId} input[type="checkbox"]`);
|
||
checkboxes.forEach(cb => {
|
||
cb.checked = types.includes(cb.value);
|
||
});
|
||
}
|
||
|
||
// ============ Init ============
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
ladePostfaecher();
|
||
ladeOrdner();
|
||
ladeRegeln();
|
||
});
|
||
|
||
// ============ BEREICH 1: Mail-Abruf ============
|
||
|
||
async function ladePostfaecher() {
|
||
try {
|
||
const postfaecher = await api('/postfaecher');
|
||
renderPostfaecher(postfaecher);
|
||
} catch (error) {
|
||
console.error('Fehler:', error);
|
||
}
|
||
}
|
||
|
||
let bearbeitetesPostfachId = null;
|
||
|
||
function renderPostfaecher(postfaecher) {
|
||
const container = document.getElementById('postfaecher-liste');
|
||
|
||
if (!postfaecher || postfaecher.length === 0) {
|
||
container.innerHTML = '<p class="empty-state">Keine Postfächer konfiguriert</p>';
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = postfaecher.map(p => `
|
||
<div class="config-item">
|
||
<div class="config-item-info">
|
||
<h4>${escapeHtml(p.name)}</h4>
|
||
<small>${escapeHtml(p.email)} → ${escapeHtml(p.ziel_ordner)}</small>
|
||
</div>
|
||
<div class="config-item-actions">
|
||
<button class="btn btn-sm" onclick="postfachAbrufen(${p.id})">Abrufen</button>
|
||
<button class="btn btn-sm" onclick="postfachBearbeiten(${p.id})">Bearbeiten</button>
|
||
<button class="btn btn-sm" onclick="postfachTesten(${p.id})">Testen</button>
|
||
<button class="btn btn-sm btn-danger" onclick="postfachLoeschen(${p.id})">×</button>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function zeigePostfachModal(postfach = null) {
|
||
bearbeitetesPostfachId = postfach?.id || null;
|
||
|
||
document.getElementById('pf-name').value = postfach?.name || '';
|
||
document.getElementById('pf-server').value = postfach?.imap_server || '';
|
||
document.getElementById('pf-port').value = postfach?.imap_port || '993';
|
||
document.getElementById('pf-email').value = postfach?.email || '';
|
||
document.getElementById('pf-passwort').value = ''; // Passwort nicht vorausfüllen
|
||
document.getElementById('pf-ordner').value = postfach?.ordner || 'INBOX';
|
||
document.getElementById('pf-alle-ordner').value = postfach?.alle_ordner ? 'true' : 'false';
|
||
document.getElementById('pf-ziel').value = postfach?.ziel_ordner || '/srv/http/dateiverwaltung/data/inbox/';
|
||
setCheckedTypes('pf-typen-gruppe', postfach?.erlaubte_typen || ['.pdf']);
|
||
document.getElementById('pf-max-groesse').value = postfach?.max_groesse_mb || '25';
|
||
|
||
document.getElementById('postfach-modal').classList.remove('hidden');
|
||
}
|
||
|
||
async function postfachBearbeiten(id) {
|
||
try {
|
||
const postfaecher = await api('/postfaecher');
|
||
const postfach = postfaecher.find(p => p.id === id);
|
||
if (postfach) {
|
||
zeigePostfachModal(postfach);
|
||
}
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function speicherePostfach() {
|
||
const erlaubteTypen = getCheckedTypes('pf-typen-gruppe');
|
||
if (erlaubteTypen.length === 0) {
|
||
alert('Bitte mindestens einen Dateityp auswählen');
|
||
return;
|
||
}
|
||
|
||
const data = {
|
||
name: document.getElementById('pf-name').value.trim(),
|
||
imap_server: document.getElementById('pf-server').value.trim(),
|
||
imap_port: parseInt(document.getElementById('pf-port').value),
|
||
email: document.getElementById('pf-email').value.trim(),
|
||
passwort: document.getElementById('pf-passwort').value,
|
||
ordner: document.getElementById('pf-ordner').value.trim(),
|
||
alle_ordner: document.getElementById('pf-alle-ordner').value === 'true',
|
||
ziel_ordner: document.getElementById('pf-ziel').value.trim(),
|
||
erlaubte_typen: erlaubteTypen,
|
||
max_groesse_mb: parseInt(document.getElementById('pf-max-groesse').value)
|
||
};
|
||
|
||
if (!data.name || !data.imap_server || !data.email || !data.ziel_ordner) {
|
||
alert('Bitte alle Pflichtfelder ausfüllen');
|
||
return;
|
||
}
|
||
|
||
// Bei Bearbeitung: Passwort nur senden wenn eingegeben
|
||
if (bearbeitetesPostfachId && !data.passwort) {
|
||
delete data.passwort;
|
||
} else if (!data.passwort) {
|
||
alert('Passwort ist erforderlich');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
if (bearbeitetesPostfachId) {
|
||
await api(`/postfaecher/${bearbeitetesPostfachId}`, { method: 'PUT', body: JSON.stringify(data) });
|
||
} else {
|
||
await api('/postfaecher', { method: 'POST', body: JSON.stringify(data) });
|
||
}
|
||
schliesseModal('postfach-modal');
|
||
ladePostfaecher();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function postfachTesten(id) {
|
||
try {
|
||
const result = await api(`/postfaecher/${id}/test`, { method: 'POST' });
|
||
alert(result.erfolg ? 'Verbindung erfolgreich!' : 'Fehler: ' + result.nachricht);
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function postfachAbrufen(id) {
|
||
const logContainer = document.getElementById('abruf-log');
|
||
logContainer.innerHTML = '<div class="log-entry info"><span>Verbinde...</span></div>';
|
||
|
||
// EventSource für Server-Sent Events
|
||
const eventSource = new EventSource(`/api/postfaecher/${id}/abrufen/stream`);
|
||
let dateiCount = 0;
|
||
let currentOrdner = '';
|
||
|
||
eventSource.onmessage = (event) => {
|
||
const data = JSON.parse(event.data);
|
||
|
||
switch (data.type) {
|
||
case 'start':
|
||
logContainer.innerHTML = `<div class="log-entry info">
|
||
<span>Starte Abruf: ${escapeHtml(data.postfach)}</span>
|
||
<small>${data.bereits_verarbeitet} bereits verarbeitet</small>
|
||
</div>`;
|
||
break;
|
||
|
||
case 'info':
|
||
logContainer.innerHTML += `<div class="log-entry info">
|
||
<span>${escapeHtml(data.nachricht)}</span>
|
||
</div>`;
|
||
break;
|
||
|
||
case 'ordner':
|
||
currentOrdner = data.name;
|
||
logContainer.innerHTML += `<div class="log-entry info" id="ordner-status">
|
||
<span>📁 ${escapeHtml(data.name)}</span>
|
||
</div>`;
|
||
break;
|
||
|
||
case 'mails':
|
||
const ordnerStatus = document.getElementById('ordner-status');
|
||
if (ordnerStatus) {
|
||
ordnerStatus.innerHTML = `<span>📁 ${escapeHtml(data.ordner)}: ${data.anzahl} Mails</span>`;
|
||
ordnerStatus.id = ''; // ID entfernen für nächsten Ordner
|
||
}
|
||
break;
|
||
|
||
case 'datei':
|
||
dateiCount++;
|
||
logContainer.innerHTML += `<div class="log-entry success">
|
||
<span>✓ ${escapeHtml(data.original_name)}</span>
|
||
<small>${formatBytes(data.groesse)}</small>
|
||
</div>`;
|
||
// Scroll nach unten
|
||
logContainer.scrollTop = logContainer.scrollHeight;
|
||
break;
|
||
|
||
case 'skip':
|
||
logContainer.innerHTML += `<div class="log-entry" style="opacity:0.6;">
|
||
<span>⊘ ${escapeHtml(data.datei)}: ${data.grund}</span>
|
||
</div>`;
|
||
break;
|
||
|
||
case 'fehler':
|
||
logContainer.innerHTML += `<div class="log-entry error">
|
||
<span>✗ ${escapeHtml(data.nachricht)}</span>
|
||
</div>`;
|
||
break;
|
||
|
||
case 'fertig':
|
||
logContainer.innerHTML += `<div class="log-entry success" style="font-weight:bold;">
|
||
<span>✓ Fertig: ${data.anzahl} Dateien gespeichert</span>
|
||
</div>`;
|
||
eventSource.close();
|
||
ladePostfaecher();
|
||
break;
|
||
}
|
||
};
|
||
|
||
eventSource.onerror = (error) => {
|
||
logContainer.innerHTML += `<div class="log-entry error">
|
||
<span>✗ Verbindung unterbrochen</span>
|
||
</div>`;
|
||
eventSource.close();
|
||
};
|
||
}
|
||
|
||
function formatBytes(bytes) {
|
||
if (bytes < 1024) return bytes + ' B';
|
||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||
}
|
||
|
||
async function allePostfaecherAbrufen() {
|
||
try {
|
||
zeigeLoading('Rufe alle Postfächer ab...');
|
||
const result = await api('/postfaecher/abrufen-alle', { method: 'POST' });
|
||
zeigeAbrufLog(result);
|
||
ladePostfaecher();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
} finally {
|
||
versteckeLoading();
|
||
}
|
||
}
|
||
|
||
async function postfachLoeschen(id) {
|
||
if (!confirm('Postfach wirklich löschen?')) return;
|
||
try {
|
||
await api(`/postfaecher/${id}`, { method: 'DELETE' });
|
||
ladePostfaecher();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
function zeigeAbrufLog(result) {
|
||
const container = document.getElementById('abruf-log');
|
||
|
||
if (!result.ergebnisse || result.ergebnisse.length === 0) {
|
||
container.innerHTML = '<p class="empty-state">Keine neuen Attachments gefunden</p>';
|
||
return;
|
||
}
|
||
|
||
let html = '';
|
||
for (const r of result.ergebnisse) {
|
||
const status = r.fehler ? 'error' : 'success';
|
||
const icon = r.fehler ? '✗' : '✓';
|
||
html += `<div class="log-entry ${status}">
|
||
<span>${icon} ${escapeHtml(r.postfach)}: ${r.anzahl || 0} Dateien</span>
|
||
${r.fehler ? `<small>${escapeHtml(r.fehler)}</small>` : ''}
|
||
</div>`;
|
||
|
||
if (r.dateien) {
|
||
for (const d of r.dateien) {
|
||
html += `<div class="log-entry info">
|
||
<span style="padding-left: 1rem;">→ ${escapeHtml(d)}</span>
|
||
</div>`;
|
||
}
|
||
}
|
||
}
|
||
|
||
container.innerHTML = html;
|
||
}
|
||
|
||
// ============ BEREICH 2: Datei-Sortierung ============
|
||
|
||
async function ladeOrdner() {
|
||
try {
|
||
const ordner = await api('/ordner');
|
||
renderOrdner(ordner);
|
||
} catch (error) {
|
||
console.error('Fehler:', error);
|
||
}
|
||
}
|
||
|
||
function renderOrdner(ordner) {
|
||
const container = document.getElementById('ordner-liste');
|
||
|
||
if (!ordner || ordner.length === 0) {
|
||
container.innerHTML = '<p class="empty-state">Keine Ordner konfiguriert</p>';
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = ordner.map(o => `
|
||
<div class="config-item">
|
||
<div class="config-item-info">
|
||
<h4>${escapeHtml(o.name)} ${o.rekursiv ? '<span class="badge badge-info">rekursiv</span>' : ''}</h4>
|
||
<small>${escapeHtml(o.pfad)} → ${escapeHtml(o.ziel_ordner)}</small>
|
||
<small style="display:block;">${(o.dateitypen || []).join(', ')}</small>
|
||
</div>
|
||
<div class="config-item-actions">
|
||
<button class="btn btn-sm" onclick="ordnerScannen(${o.id})">Scannen</button>
|
||
<button class="btn btn-sm btn-danger" onclick="ordnerLoeschen(${o.id})">×</button>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function zeigeOrdnerModal() {
|
||
document.getElementById('ord-name').value = '';
|
||
document.getElementById('ord-pfad').value = '/srv/http/dateiverwaltung/data/inbox/';
|
||
document.getElementById('ord-ziel').value = '/srv/http/dateiverwaltung/data/archiv/';
|
||
setCheckedTypes('ord-typen-gruppe', ['.pdf', '.jpg', '.jpeg', '.png', '.tiff']);
|
||
document.getElementById('ord-rekursiv').value = 'true';
|
||
document.getElementById('ordner-modal').classList.remove('hidden');
|
||
}
|
||
|
||
async function speichereOrdner() {
|
||
const dateitypen = getCheckedTypes('ord-typen-gruppe');
|
||
if (dateitypen.length === 0) {
|
||
alert('Bitte mindestens einen Dateityp auswählen');
|
||
return;
|
||
}
|
||
|
||
const data = {
|
||
name: document.getElementById('ord-name').value.trim(),
|
||
pfad: document.getElementById('ord-pfad').value.trim(),
|
||
ziel_ordner: document.getElementById('ord-ziel').value.trim(),
|
||
rekursiv: document.getElementById('ord-rekursiv').value === 'true',
|
||
dateitypen: dateitypen
|
||
};
|
||
|
||
if (!data.name || !data.pfad || !data.ziel_ordner) {
|
||
alert('Bitte alle Felder ausfüllen');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
zeigeLoading('Speichere Ordner...');
|
||
await api('/ordner', { method: 'POST', body: JSON.stringify(data) });
|
||
schliesseModal('ordner-modal');
|
||
ladeOrdner();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
} finally {
|
||
versteckeLoading();
|
||
}
|
||
}
|
||
|
||
async function ordnerLoeschen(id) {
|
||
if (!confirm('Ordner wirklich löschen?')) return;
|
||
try {
|
||
await api(`/ordner/${id}`, { method: 'DELETE' });
|
||
ladeOrdner();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function ordnerScannen(id) {
|
||
try {
|
||
const result = await api(`/ordner/${id}/scannen`);
|
||
alert(`${result.anzahl} Dateien im Ordner gefunden`);
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// ============ Regeln ============
|
||
|
||
let editierteRegelId = null;
|
||
|
||
async function ladeRegeln() {
|
||
try {
|
||
const regeln = await api('/regeln');
|
||
renderRegeln(regeln);
|
||
} catch (error) {
|
||
console.error('Fehler:', error);
|
||
}
|
||
}
|
||
|
||
function renderRegeln(regeln) {
|
||
const container = document.getElementById('regeln-liste');
|
||
|
||
if (!regeln || regeln.length === 0) {
|
||
container.innerHTML = '<p class="empty-state">Keine Regeln definiert</p>';
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = regeln.map(r => `
|
||
<div class="config-item">
|
||
<div class="config-item-info">
|
||
<h4>${escapeHtml(r.name)} <span class="badge badge-info">Prio ${r.prioritaet}</span></h4>
|
||
<small>${escapeHtml(r.schema)}</small>
|
||
</div>
|
||
<div class="config-item-actions">
|
||
<button class="btn btn-sm" onclick="bearbeiteRegel(${r.id})">Bearbeiten</button>
|
||
<button class="btn btn-sm btn-danger" onclick="regelLoeschen(${r.id})">×</button>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function zeigeRegelModal(regel = null) {
|
||
editierteRegelId = regel?.id || null;
|
||
document.getElementById('regel-modal-title').textContent = regel ? 'Regel bearbeiten' : 'Regel hinzufügen';
|
||
|
||
document.getElementById('regel-name').value = regel?.name || '';
|
||
document.getElementById('regel-prioritaet').value = regel?.prioritaet || 100;
|
||
document.getElementById('regel-muster').value = JSON.stringify(regel?.muster || {"text_match_any": [], "text_match": []}, null, 2);
|
||
document.getElementById('regel-extraktion').value = JSON.stringify(regel?.extraktion || {}, null, 2);
|
||
document.getElementById('regel-schema').value = regel?.schema || '{datum} - Dokument.pdf';
|
||
document.getElementById('regel-unterordner').value = regel?.unterordner || '';
|
||
document.getElementById('regel-test-text').value = '';
|
||
document.getElementById('regel-test-ergebnis').classList.add('hidden');
|
||
|
||
document.getElementById('regel-modal').classList.remove('hidden');
|
||
}
|
||
|
||
async function bearbeiteRegel(id) {
|
||
try {
|
||
const regeln = await api('/regeln');
|
||
const regel = regeln.find(r => r.id === id);
|
||
if (regel) zeigeRegelModal(regel);
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function speichereRegel() {
|
||
let muster, extraktion;
|
||
|
||
try {
|
||
muster = JSON.parse(document.getElementById('regel-muster').value);
|
||
} catch (e) {
|
||
alert('Ungültiges JSON im Muster-Feld');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
extraktion = JSON.parse(document.getElementById('regel-extraktion').value);
|
||
} catch (e) {
|
||
alert('Ungültiges JSON im Extraktion-Feld');
|
||
return;
|
||
}
|
||
|
||
const data = {
|
||
name: document.getElementById('regel-name').value.trim(),
|
||
prioritaet: parseInt(document.getElementById('regel-prioritaet').value),
|
||
muster,
|
||
extraktion,
|
||
schema: document.getElementById('regel-schema').value.trim(),
|
||
unterordner: document.getElementById('regel-unterordner').value.trim() || null
|
||
};
|
||
|
||
if (!data.name) {
|
||
alert('Bitte einen Namen eingeben');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
if (editierteRegelId) {
|
||
await api(`/regeln/${editierteRegelId}`, { method: 'PUT', body: JSON.stringify(data) });
|
||
} else {
|
||
await api('/regeln', { method: 'POST', body: JSON.stringify(data) });
|
||
}
|
||
schliesseModal('regel-modal');
|
||
ladeRegeln();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function regelLoeschen(id) {
|
||
if (!confirm('Regel wirklich löschen?')) return;
|
||
try {
|
||
await api(`/regeln/${id}`, { method: 'DELETE' });
|
||
ladeRegeln();
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
async function testeRegel() {
|
||
const text = document.getElementById('regel-test-text').value;
|
||
if (!text) {
|
||
alert('Bitte Testtext eingeben');
|
||
return;
|
||
}
|
||
|
||
let muster, extraktion;
|
||
try {
|
||
muster = JSON.parse(document.getElementById('regel-muster').value);
|
||
extraktion = JSON.parse(document.getElementById('regel-extraktion').value);
|
||
} catch (e) {
|
||
alert('Ungültiges JSON');
|
||
return;
|
||
}
|
||
|
||
const regel = {
|
||
name: 'Test',
|
||
muster,
|
||
extraktion,
|
||
schema: document.getElementById('regel-schema').value.trim()
|
||
};
|
||
|
||
try {
|
||
const result = await api('/regeln/test', {
|
||
method: 'POST',
|
||
body: JSON.stringify({ regel, text })
|
||
});
|
||
|
||
const container = document.getElementById('regel-test-ergebnis');
|
||
container.classList.remove('hidden', 'success', 'error');
|
||
|
||
if (result.passt) {
|
||
container.classList.add('success');
|
||
container.textContent = `✓ Regel passt!\n\nExtrahiert:\n${JSON.stringify(result.extrahiert, null, 2)}\n\nDateiname:\n${result.dateiname}`;
|
||
} else {
|
||
container.classList.add('error');
|
||
container.textContent = '✗ Regel passt nicht';
|
||
}
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// ============ Sortierung starten ============
|
||
|
||
async function sortierungStarten() {
|
||
try {
|
||
zeigeLoading('Sortiere Dateien...');
|
||
const result = await api('/sortierung/starten', { method: 'POST' });
|
||
zeigeSortierungLog(result);
|
||
} catch (error) {
|
||
alert('Fehler: ' + error.message);
|
||
} finally {
|
||
versteckeLoading();
|
||
}
|
||
}
|
||
|
||
function zeigeSortierungLog(result) {
|
||
const container = document.getElementById('sortierung-log');
|
||
|
||
if (!result.verarbeitet || result.verarbeitet.length === 0) {
|
||
container.innerHTML = '<p class="empty-state">Keine Dateien verarbeitet</p>';
|
||
return;
|
||
}
|
||
|
||
let html = `<div class="log-entry info">
|
||
<span>Gesamt: ${result.gesamt} | Sortiert: ${result.sortiert} | ZUGFeRD: ${result.zugferd} | Fehler: ${result.fehler}</span>
|
||
</div>`;
|
||
|
||
for (const d of result.verarbeitet) {
|
||
const status = d.fehler ? 'error' : (d.zugferd ? 'info' : 'success');
|
||
const icon = d.fehler ? '✗' : (d.zugferd ? '🧾' : '✓');
|
||
html += `<div class="log-entry ${status}">
|
||
<span>${icon} ${escapeHtml(d.neuer_name || d.original)}</span>
|
||
${d.fehler ? `<small>${escapeHtml(d.fehler)}</small>` : ''}
|
||
</div>`;
|
||
}
|
||
|
||
container.innerHTML = html;
|
||
}
|
||
|
||
// ============ Utilities ============
|
||
|
||
function schliesseModal(id) {
|
||
document.getElementById(id).classList.add('hidden');
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
if (!text) return '';
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
document.addEventListener('click', (e) => {
|
||
if (e.target.classList.contains('modal')) {
|
||
e.target.classList.add('hidden');
|
||
}
|
||
});
|
||
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape') {
|
||
document.querySelectorAll('.modal:not(.hidden)').forEach(m => m.classList.add('hidden'));
|
||
}
|
||
});
|