937 lines
54 KiB
HTML
Executable file
937 lines
54 KiB
HTML
Executable file
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Dateiverwaltung</title>
|
||
<link rel="stylesheet" href="/static/css/style.css">
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<!-- Header -->
|
||
<header class="header">
|
||
<div class="header-left">
|
||
<h1>Dateiverwaltung</h1>
|
||
</div>
|
||
<div class="header-right">
|
||
<span id="status-indicator"></span>
|
||
<button class="btn-icon" onclick="zeigeLogModal()" title="Debug-Log">📋</button>
|
||
<button class="btn-icon" onclick="zeigeEinstellungenModal()" title="Einstellungen">⚙️</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Main Content -->
|
||
<div class="main-container">
|
||
<!-- Bereich 1: Mail-Abruf -->
|
||
<section class="bereich">
|
||
<div class="bereich-header">
|
||
<h2>📧 Mail-Abruf</h2>
|
||
<p class="bereich-desc">Attachments aus Postfächern in Ordner speichern</p>
|
||
</div>
|
||
|
||
<div class="bereich-content">
|
||
<!-- Postfächer Liste -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Postfächer</h3>
|
||
<button class="btn btn-sm btn-primary" onclick="zeigePostfachModal()">+ Hinzufügen</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="postfaecher-liste">
|
||
<p class="empty-state">Keine Postfächer konfiguriert</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Abruf starten -->
|
||
<div class="action-bar">
|
||
<button class="btn btn-success btn-large" onclick="allePostfaecherAbrufen()">
|
||
▶ Alle Postfächer abrufen
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Letzter Abruf Log -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Letzter Abruf</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="abruf-log" class="log-output">
|
||
<p class="empty-state">Noch kein Abruf durchgeführt</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Bereich 2: Datei-Sortierung -->
|
||
<section class="bereich">
|
||
<div class="bereich-header">
|
||
<h2>📁 Datei-Sortierung</h2>
|
||
<p class="bereich-desc">Dateien nach Regeln umbenennen und verschieben</p>
|
||
</div>
|
||
|
||
<div class="bereich-content">
|
||
<!-- Grobsortierung -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Grobsortierung</h3>
|
||
<button class="btn btn-sm btn-primary" onclick="zeigeOrdnerModal()">+ Hinzufügen</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="ordner-liste">
|
||
<p class="empty-state">Keine Ordner konfiguriert</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Regeln -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Sortier-Regeln</h3>
|
||
<button class="btn btn-sm btn-primary" onclick="zeigeRegelModal()">+ Hinzufügen</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="regeln-liste">
|
||
<p class="empty-state">Keine Regeln definiert</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sortierung starten -->
|
||
<div class="action-bar">
|
||
<button class="btn btn-success btn-large" onclick="sortierungStarten()">
|
||
▶ Sortierung starten
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Sortierungs-Log -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Verarbeitete Dateien</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="sortierung-log" class="log-output">
|
||
<p class="empty-state">Noch keine Dateien verarbeitet</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Bereich 3: Zeitpläne / Scheduler -->
|
||
<section class="bereich">
|
||
<div class="bereich-header">
|
||
<h2>⏰ Zeitpläne</h2>
|
||
<p class="bereich-desc">Automatische Ausführung von Mail-Abruf und Sortierung</p>
|
||
</div>
|
||
|
||
<div class="bereich-content">
|
||
<!-- Status-Übersicht -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Status-Übersicht</h3>
|
||
<button class="btn btn-sm" onclick="ladeStatus()">🔄 Aktualisieren</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="status-uebersicht">
|
||
<p class="empty-state">Status wird geladen...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Zeitpläne Liste -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Zeitpläne</h3>
|
||
<button class="btn btn-sm btn-primary" onclick="zeigeZeitplanModal()">+ Hinzufügen</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="zeitplaene-liste">
|
||
<p class="empty-state">Keine Zeitpläne konfiguriert</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- Modal: Postfach hinzufügen -->
|
||
<div id="postfach-modal" class="modal hidden">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Postfach hinzufügen</h3>
|
||
<button class="modal-close" onclick="schliesseModal('postfach-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label>Name</label>
|
||
<input type="text" id="pf-name" placeholder="z.B. Firma Rechnungen">
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>IMAP Server</label>
|
||
<input type="text" id="pf-server" placeholder="imap.example.com">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Port</label>
|
||
<input type="number" id="pf-port" value="993">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>E-Mail</label>
|
||
<input type="email" id="pf-email" placeholder="mail@example.com">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Passwort</label>
|
||
<input type="password" id="pf-passwort">
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>IMAP-Ordner</label>
|
||
<input type="text" id="pf-ordner" value="INBOX">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Alle Ordner durchsuchen</label>
|
||
<select id="pf-alle-ordner">
|
||
<option value="false">Nein (nur angegebenen Ordner)</option>
|
||
<option value="true">Ja (alle Ordner)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Welche Mails durchsuchen</label>
|
||
<select id="pf-nur-ungelesen">
|
||
<option value="false" selected>Alle Mails</option>
|
||
<option value="true">Nur ungelesene Mails</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Mails ab Datum</label>
|
||
<input type="date" id="pf-ab-datum">
|
||
<small>Nur Mails ab diesem Datum verarbeiten (leer = alle)</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Ziel-Ordner</label>
|
||
<div class="input-with-btn">
|
||
<input type="text" id="pf-ziel" value="/srv/http/dateiverwaltung/data/inbox/">
|
||
<button class="btn" type="button" onclick="oeffneBrowser('pf-ziel')">📁</button>
|
||
</div>
|
||
<small>Hier landen die Attachments</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Erlaubte Dateitypen</label>
|
||
<div class="checkbox-group" id="pf-typen-gruppe">
|
||
<label class="checkbox-item"><input type="checkbox" value=".pdf" checked> PDF</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".jpg"> JPG</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".jpeg"> JPEG</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".png"> PNG</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".gif"> GIF</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".tiff"> TIFF</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".doc"> DOC</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".docx"> DOCX</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".xls"> XLS</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".xlsx"> XLSX</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".csv"> CSV</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".txt"> TXT</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".zip"> ZIP</label>
|
||
<label class="checkbox-item"><input type="checkbox" value=".xml"> XML</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Standard Max. Größe (MB)</label>
|
||
<input type="number" id="pf-max-groesse" value="25" style="width: 100px;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Standard Min. Größe (KB)</label>
|
||
<input type="number" id="pf-min-groesse" value="10" style="width: 100px;">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Größenfilter pro Dateityp (optional)</label>
|
||
<small>Überschreibt die Standard-Werte für einzelne Dateitypen</small>
|
||
<div id="pf-groessen-filter" class="groessen-filter-container">
|
||
<!-- Dynamisch generiert -->
|
||
</div>
|
||
<button type="button" class="btn btn-sm" onclick="toggleGroessenFilter()">
|
||
Größenfilter pro Typ anzeigen/bearbeiten
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn" onclick="schliesseModal('postfach-modal')">Abbrechen</button>
|
||
<button class="btn btn-primary" onclick="speicherePostfach()">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Ordner hinzufügen/bearbeiten - Breit mit 2 Spalten -->
|
||
<div id="ordner-modal" class="modal hidden">
|
||
<div class="modal-content modal-fullwidth">
|
||
<div class="modal-header">
|
||
<h3 id="ordner-modal-title">Grobsortierung hinzufügen</h3>
|
||
<button class="modal-close" onclick="schliesseModal('ordner-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body ordner-editor-body">
|
||
<!-- LINKE SPALTE: Grundeinstellungen -->
|
||
<div class="ordner-spalte">
|
||
<h4>📁 Grundeinstellungen</h4>
|
||
|
||
<div class="form-group">
|
||
<label title="Eindeutiger Name zur Identifikation dieser Grobsortierung">Name</label>
|
||
<input type="text" id="ord-name" placeholder="z.B. Firma Inbox" title="Gib der Grobsortierung einen Namen, z.B. 'E-Mail Anhänge' oder 'Scanner Eingang'">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label title="Ordner in dem neue Dateien eingehen (z.B. Inbox, Download-Ordner)">Quell-Pfad (wo liegen die Dateien?)</label>
|
||
<div class="input-with-btn">
|
||
<input type="text" id="ord-pfad" value="/srv/http/dateiverwaltung/data/inbox/" title="Absoluter Pfad zum Ordner der überwacht werden soll">
|
||
<button class="btn" type="button" onclick="oeffneBrowser('ord-pfad')" title="Ordner auswählen">📁</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label title="Ordner in den die Dateien nach der Grobsortierung verschoben werden">Ziel-Ordner (wohin nach Sortierung?)</label>
|
||
<div class="input-with-btn">
|
||
<input type="text" id="ord-ziel" value="/srv/http/dateiverwaltung/data/archiv/" title="Hier landen die Dateien nach der Grobsortierung. Sortierregeln greifen dann auf diesen Ordner zu.">
|
||
<button class="btn" type="button" onclick="oeffneBrowser('ord-ziel')" title="Ordner auswählen">📁</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label title="Sollen auch Dateien in Unterordnern verarbeitet werden?">Unterordner einschließen</label>
|
||
<select id="ord-rekursiv" title="Ja = alle Unterordner werden durchsucht. Nein = nur der Hauptordner.">
|
||
<option value="true" selected>Ja (rekursiv)</option>
|
||
<option value="false">Nein (nur dieser Ordner)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Sortier-Modus -->
|
||
<div class="ordner-section">
|
||
<h4 title="Wie sollen die Dateien verarbeitet werden?">Sortier-Modus</h4>
|
||
<div class="radio-group">
|
||
<label class="radio-item" title="Dateien werden analysiert und mit Sortierregeln verarbeitet">
|
||
<input type="radio" name="ord-modus" value="regeln" checked>
|
||
<span>Mit Regeln sortieren</span>
|
||
</label>
|
||
<label class="radio-item" title="Dateien werden direkt in den Zielordner verschoben, ohne Regeln anzuwenden">
|
||
<input type="radio" name="ord-modus" value="direkt">
|
||
<span>Direkt verschieben (ohne Regeln)</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MITTLERE SPALTE: Dateitypen -->
|
||
<div class="ordner-spalte">
|
||
<h4 title="Welche Dateitypen sollen verarbeitet werden?">📄 Dateitypen</h4>
|
||
<div class="checkbox-group dateitypen-grid" id="ord-typen-gruppe">
|
||
<label class="checkbox-item" title="PDF-Dokumente (Textextraktion + OCR möglich)"><input type="checkbox" value=".pdf" checked> PDF</label>
|
||
<label class="checkbox-item" title="JPEG-Bilder"><input type="checkbox" value=".jpg" checked> JPG</label>
|
||
<label class="checkbox-item" title="JPEG-Bilder"><input type="checkbox" value=".jpeg" checked> JPEG</label>
|
||
<label class="checkbox-item" title="PNG-Bilder"><input type="checkbox" value=".png" checked> PNG</label>
|
||
<label class="checkbox-item" title="GIF-Bilder"><input type="checkbox" value=".gif"> GIF</label>
|
||
<label class="checkbox-item" title="TIFF-Bilder (oft von Scannern)"><input type="checkbox" value=".tiff" checked> TIFF</label>
|
||
<label class="checkbox-item" title="Bitmap-Bilder"><input type="checkbox" value=".bmp"> BMP</label>
|
||
<label class="checkbox-item" title="Word-Dokumente (alt)"><input type="checkbox" value=".doc"> DOC</label>
|
||
<label class="checkbox-item" title="Word-Dokumente (neu)"><input type="checkbox" value=".docx"> DOCX</label>
|
||
<label class="checkbox-item" title="Excel-Dateien (alt)"><input type="checkbox" value=".xls"> XLS</label>
|
||
<label class="checkbox-item" title="Excel-Dateien (neu)"><input type="checkbox" value=".xlsx"> XLSX</label>
|
||
<label class="checkbox-item" title="CSV-Dateien"><input type="checkbox" value=".csv"> CSV</label>
|
||
<label class="checkbox-item" title="Text-Dateien"><input type="checkbox" value=".txt"> TXT</label>
|
||
<label class="checkbox-item" title="XML-Dateien"><input type="checkbox" value=".xml"> XML</label>
|
||
</div>
|
||
|
||
<!-- Besondere Dateiarten -->
|
||
<div class="ordner-section" style="margin-top: 1.5rem;">
|
||
<h4 title="Spezielle Behandlung für bestimmte Dokumenttypen">🧾 Besondere Dateiarten</h4>
|
||
<div class="checkbox-group dateitypen-grid">
|
||
<label class="checkbox-item" title="ZUGFeRD-Rechnungen in separaten Unterordner verschieben"><input type="checkbox" id="ord-zugferd-sep" checked> ZUGFeRD</label>
|
||
<label class="checkbox-item" title="Digital signierte PDFs in separaten Unterordner verschieben"><input type="checkbox" id="ord-signiert-sep"> Signiert</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- RECHTE SPALTE: PDF-Verarbeitung -->
|
||
<div class="ordner-spalte">
|
||
<h4 title="Einstellungen für die PDF-Texterkennung">⚙️ PDF-Verarbeitung</h4>
|
||
|
||
<div class="ordner-section">
|
||
<label class="checkbox-label" title="OCR (Texterkennung) für gescannte PDFs aktivieren">
|
||
<input type="checkbox" id="ord-ocr" checked>
|
||
<span>OCR aktivieren</span>
|
||
</label>
|
||
<small style="color: var(--text-secondary); display: block; margin-top: 0.25rem;">
|
||
Gescannte PDFs werden durchsuchbar gemacht
|
||
</small>
|
||
</div>
|
||
|
||
<div class="form-group" style="margin-top: 1rem;">
|
||
<label title="Optionaler Backup-Ordner für Originale vor OCR-Verarbeitung">Original sichern vor OCR (optional)</label>
|
||
<div class="input-with-btn">
|
||
<input type="text" id="ord-original-sichern" placeholder="Leer = kein Backup" title="Wenn angegeben, wird das Original vor der OCR-Verarbeitung hierhin kopiert">
|
||
<button class="btn" type="button" onclick="oeffneBrowser('ord-original-sichern')" title="Ordner auswählen">📁</button>
|
||
</div>
|
||
<small style="color: var(--text-secondary);">Das Original wird vor OCR hierhin kopiert</small>
|
||
</div>
|
||
|
||
<!-- Signatur-Prüfung Info -->
|
||
<div class="ordner-section" style="margin-top: 1.5rem;">
|
||
<h4>ℹ️ Info</h4>
|
||
<div style="font-size: 0.85rem; color: var(--text-secondary);">
|
||
<p><strong>ZUGFeRD:</strong> Elektronische Rechnungen mit eingebettetem XML. Enthalten strukturierte Daten für automatische Verarbeitung.</p>
|
||
<p style="margin-top: 0.5rem;"><strong>Signierte PDFs:</strong> Dokumente mit digitaler Unterschrift. Bei Änderung wird die Signatur ungültig - OCR wird übersprungen.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn" onclick="schliesseModal('ordner-modal')">Abbrechen</button>
|
||
<button class="btn btn-primary" onclick="speichereOrdner()">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Regel hinzufügen/bearbeiten - 3 Spalten Layout -->
|
||
<div id="regel-modal" class="modal hidden">
|
||
<div class="modal-content modal-fullwidth">
|
||
<div class="modal-header">
|
||
<h3 id="regel-modal-title">Regel hinzufügen</h3>
|
||
<button class="modal-close" onclick="schliesseModal('regel-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body regel-editor-body">
|
||
<!-- LINKS: Regex-Hilfe -->
|
||
<div class="regel-spalte regex-hilfe">
|
||
<h4>📚 Regex-Hilfe</h4>
|
||
<!-- Regex aus Markierung Button -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<button class="btn btn-sm btn-primary" onclick="regexAusMarkierung()" title="Text im PDF markieren, dann klicken">
|
||
🎯 Regex aus Markierung
|
||
</button>
|
||
<div id="regex-helfer-ergebnis" class="hidden"></div>
|
||
</div>
|
||
<div class="regex-cheatsheet">
|
||
<div class="regex-gruppe">
|
||
<strong>Zeichen</strong>
|
||
<div class="regex-item"><code>.</code> Beliebiges Zeichen</div>
|
||
<div class="regex-item"><code>\d</code> Ziffer (0-9)</div>
|
||
<div class="regex-item"><code>\w</code> Wortzeichen (a-z, 0-9, _)</div>
|
||
<div class="regex-item"><code>\s</code> Whitespace (Leer, Tab)</div>
|
||
<div class="regex-item"><code>\S</code> Nicht-Whitespace</div>
|
||
</div>
|
||
<div class="regex-gruppe">
|
||
<strong>Mengen</strong>
|
||
<div class="regex-item"><code>*</code> 0 oder mehr</div>
|
||
<div class="regex-item"><code>+</code> 1 oder mehr</div>
|
||
<div class="regex-item"><code>?</code> 0 oder 1</div>
|
||
<div class="regex-item"><code>{3}</code> Genau 3 mal</div>
|
||
<div class="regex-item"><code>{2,4}</code> 2 bis 4 mal</div>
|
||
</div>
|
||
<div class="regex-gruppe">
|
||
<strong>Gruppen</strong>
|
||
<div class="regex-item"><code>(...)</code> Erfassungsgruppe</div>
|
||
<div class="regex-item"><code>[abc]</code> a, b oder c</div>
|
||
<div class="regex-item"><code>[0-9]</code> Ziffer</div>
|
||
<div class="regex-item"><code>[^abc]</code> Nicht a, b, c</div>
|
||
<div class="regex-item"><code>a|b</code> a oder b</div>
|
||
</div>
|
||
<div class="regex-gruppe">
|
||
<strong>Anker</strong>
|
||
<div class="regex-item"><code>^</code> Zeilenanfang</div>
|
||
<div class="regex-item"><code>$</code> Zeilenende</div>
|
||
<div class="regex-item"><code>\b</code> Wortgrenze</div>
|
||
</div>
|
||
<div class="regex-gruppe">
|
||
<strong>Escape</strong>
|
||
<div class="regex-item"><code>\.</code> Punkt literal</div>
|
||
<div class="regex-item"><code>\/</code> Slash literal</div>
|
||
<div class="regex-item"><code>\-</code> Minus literal</div>
|
||
</div>
|
||
<div class="regex-gruppe">
|
||
<strong>Beispiele</strong>
|
||
<div class="regex-beispiel">
|
||
<code>\d{2}\.\d{2}\.\d{4}</code>
|
||
<small>Datum: 31.12.2024</small>
|
||
</div>
|
||
<div class="regex-beispiel">
|
||
<code>[\d.,]+\s*€</code>
|
||
<small>Betrag: 123,45 €</small>
|
||
</div>
|
||
<div class="regex-beispiel">
|
||
<code>RE-?\d{4,}</code>
|
||
<small>Nummer: RE-12345</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MITTE: Eingabefelder -->
|
||
<div class="regel-spalte regel-eingabe">
|
||
<!-- Grundeinstellungen kompakt -->
|
||
<div class="regel-section">
|
||
<div class="form-row">
|
||
<div class="form-group" style="flex: 2;">
|
||
<label title="Eindeutiger Name zur Identifikation der Regel">Name</label>
|
||
<input type="text" id="regel-name" placeholder="z.B. Sonepar Rechnung" title="Gib der Regel einen aussagekräftigen Namen, z.B. 'Sonepar Rechnung' oder 'Telekom Vertrag'">
|
||
</div>
|
||
<div class="form-group" style="flex: 1;">
|
||
<label title="Niedrigere Zahl = höhere Priorität. Regeln werden in dieser Reihenfolge geprüft.">Priorität</label>
|
||
<input type="number" id="regel-prioritaet" value="100" title="1-999: Niedrig = wichtig. Regel mit Prio 10 wird vor Regel mit Prio 100 geprüft.">
|
||
</div>
|
||
</div>
|
||
<div class="form-row" style="margin-top: 0.5rem; gap: 1rem;">
|
||
<label class="checkbox-label compact" title="Fallback-Regeln greifen nur wenn keine andere Regel passt">
|
||
<input type="checkbox" id="regel-ist-fallback">
|
||
<span>Fallback</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Erkennung -->
|
||
<div class="regel-section">
|
||
<h4 title="Bedingungen die erfüllt sein müssen damit die Regel greift">Erkennung</h4>
|
||
<div class="form-group">
|
||
<label title="Komma-getrennte Wörter die ALLE im Dokument vorkommen müssen">Keywords (müssen vorkommen)</label>
|
||
<input type="text" id="regel-keywords" placeholder="rechnung, sonepar" title="Alle Keywords müssen im PDF-Text enthalten sein (Groß/Klein egal). Beispiel: 'rechnung, sonepar' matched 'Rechnung von Sonepar'">
|
||
</div>
|
||
<div class="form-group">
|
||
<label title="Komma-getrennte Wörter die NICHT im Dokument vorkommen dürfen">Ausschluss-Keywords</label>
|
||
<input type="text" id="regel-keywords-nicht" placeholder="gutschrift, storno" title="Wenn eines dieser Wörter vorkommt, greift die Regel NICHT. Nützlich um z.B. Gutschriften von Rechnungen zu unterscheiden.">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Feld-Extraktion -->
|
||
<div class="regel-section">
|
||
<h4>Feld-Extraktion</h4>
|
||
<table class="extraktion-tabelle compact" id="extraktion-tabelle">
|
||
<thead>
|
||
<tr>
|
||
<th>Feldname</th>
|
||
<th>Typ</th>
|
||
<th>Regex-Muster / Fester Wert</th>
|
||
<th title="Bei mehreren Treffern">Auswahl</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="extraktion-tbody">
|
||
<!-- Wird dynamisch befüllt -->
|
||
</tbody>
|
||
</table>
|
||
<button type="button" class="btn btn-sm" onclick="fuegeExtraktionsFeldHinzu()">+ Feld</button>
|
||
</div>
|
||
|
||
<!-- Ausgabe -->
|
||
<div class="regel-section">
|
||
<h4 title="Wie und wohin die Dateien sortiert werden">Ausgabe</h4>
|
||
<div class="form-group">
|
||
<label class="checkbox-label" title="Datei wird nur umbenannt, bleibt aber im gleichen Ordner">
|
||
<input type="checkbox" id="regel-nur-umbenennen" onchange="toggleZielOrdnerGruppe()">
|
||
<span>Nur umbenennen (nicht verschieben)</span>
|
||
</label>
|
||
<small style="display: block; margin-top: 0.25rem; color: var(--text-secondary);">
|
||
Dateien bleiben im Quellordner und werden nur umbenannt
|
||
</small>
|
||
</div>
|
||
<div class="form-group" id="ziel-ordner-gruppe">
|
||
<label title="Hauptordner in den die Dateien verschoben werden">Ziel-Ordner</label>
|
||
<div class="input-with-btn">
|
||
<input type="text" id="regel-ziel-ordner" placeholder="Wo sollen die Dateien hin?" title="Absoluter Pfad zum Zielordner, z.B. /mnt/user/Dokumente/Rechnungen">
|
||
<button class="btn" type="button" onclick="oeffneBrowser('regel-ziel-ordner')" title="Ordner auswählen">📁</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label title="So wird die Datei benannt. Platzhalter werden durch extrahierte Werte ersetzt.">Dateiname-Schema</label>
|
||
<input type="text" id="regel-schema" value="{datum} - Rechnung - {firma} - {nummer} - {betrag} EUR.pdf" title="Verfügbare Platzhalter: {datum}, {firma}, {nummer}, {betrag}, {typ}. Fehlende Felder werden automatisch weggelassen.">
|
||
<small>Platzhalter: {datum}, {firma}, {nummer}, {betrag}, {typ}</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label title="Optionaler Unterordner innerhalb des Ziel-Ordners">Unterordner (optional)</label>
|
||
<input type="text" id="regel-unterordner" placeholder="rechnungen/sonepar" title="Wird dem Ziel-Ordner angehängt. Kann mehrere Ebenen haben: firma/rechnungen/2024">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ordner-Zuweisung -->
|
||
<div class="regel-section">
|
||
<h4>Ordner-Zuweisung</h4>
|
||
<div id="regel-ordner-liste" class="ordner-checkboxen compact">
|
||
<p style="color: var(--text-secondary);">Lade...</p>
|
||
</div>
|
||
<details style="margin-top: 0.5rem;">
|
||
<summary style="cursor: pointer; color: var(--text-secondary);">+ Freie Ordner</summary>
|
||
<div id="regel-freie-ordner" class="freie-ordner-liste" style="margin-top: 0.5rem;"></div>
|
||
<div class="input-with-btn" style="margin-top: 0.5rem;">
|
||
<input type="text" id="regel-neuer-ordner" placeholder="/pfad/zum/ordner/">
|
||
<button class="btn" type="button" onclick="oeffneBrowser('regel-neuer-ordner')">📁</button>
|
||
<button class="btn btn-primary" type="button" onclick="fuegeFreienOrdnerHinzu()">+</button>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
|
||
<!-- Versteckte Felder -->
|
||
<input type="hidden" id="regel-muster">
|
||
<input type="hidden" id="regel-extraktion">
|
||
<input type="hidden" id="regel-text-regex" value="">
|
||
</div>
|
||
|
||
<!-- RECHTS: Live-Vorschau -->
|
||
<div class="regel-spalte regel-vorschau">
|
||
<h4>📄 Live-Vorschau</h4>
|
||
<div class="test-controls">
|
||
<input type="file" id="regel-test-datei" accept=".pdf" onchange="ladeTestPDF()" style="display:none">
|
||
<button class="btn btn-sm" onclick="document.getElementById('regel-test-datei').click()">📄 PDF laden</button>
|
||
<button class="btn btn-sm btn-success" onclick="autoRegexGenerieren()">🔮 Auto</button>
|
||
<button class="btn btn-sm btn-primary" onclick="testeRegelLive()">🔍 Testen</button>
|
||
</div>
|
||
<div id="test-datei-name" style="font-size: 0.8rem; color: var(--text-secondary); margin: 0.5rem 0;"></div>
|
||
|
||
<!-- PDF-Text Anzeige -->
|
||
<div class="pdf-text-container">
|
||
<div id="regel-test-text-display" class="pdf-text-display" contenteditable="false">
|
||
<p style="color: var(--text-secondary); text-align: center; padding: 2rem;">
|
||
PDF hochladen um Text anzuzeigen
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Test-Ergebnisse -->
|
||
<div id="regel-test-ergebnis" class="test-result hidden">
|
||
<div id="test-status" class="test-status-box"></div>
|
||
<div id="test-extrahiert" class="test-extrahiert-box"></div>
|
||
<div id="test-dateiname" class="test-dateiname-box" style="display: none;"></div>
|
||
</div>
|
||
|
||
<!-- Verstecktes Textarea für Kompatibilität -->
|
||
<textarea id="regel-test-text" style="display:none;"></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn" onclick="schliesseModal('regel-modal')">Abbrechen</button>
|
||
<button class="btn btn-primary" onclick="speichereRegel()">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Verzeichnis-Browser -->
|
||
<div id="browser-modal" class="modal hidden">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Verzeichnis wählen</h3>
|
||
<button class="modal-close" onclick="schliesseModal('browser-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="file-browser">
|
||
<div class="file-browser-path">
|
||
<input type="text" id="browser-path-input" value="/"
|
||
onkeydown="if(event.key==='Enter'){navigiereToPfad();}"
|
||
placeholder="Pfad eingeben...">
|
||
<button class="btn btn-sm" onclick="navigiereToPfad()" title="Zu Pfad navigieren">↵</button>
|
||
</div>
|
||
<ul class="file-browser-list" id="browser-list"></ul>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn" onclick="schliesseModal('browser-modal')">Abbrechen</button>
|
||
<button class="btn btn-primary" onclick="browserAuswahl()">Auswählen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Zeitplan hinzufügen -->
|
||
<div id="zeitplan-modal" class="modal hidden">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3 id="zeitplan-modal-title">Zeitplan hinzufügen</h3>
|
||
<button class="modal-close" onclick="schliesseModal('zeitplan-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label>Name</label>
|
||
<input type="text" id="zp-name" placeholder="z.B. Täglicher Mail-Abruf">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Was soll ausgeführt werden?</label>
|
||
<select id="zp-typ" onchange="zeitplanTypChanged()">
|
||
<option value="mail_abruf">Mail-Abruf</option>
|
||
<option value="grobsortierung">Grobsortierung</option>
|
||
<option value="sortierregeln">Nur Sortierregeln</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group" id="zp-postfach-gruppe">
|
||
<label>Postfach (leer = alle aktiven)</label>
|
||
<select id="zp-postfach">
|
||
<option value="">Alle aktiven Postfächer</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group hidden" id="zp-ordner-gruppe">
|
||
<label>Grobsortierung (leer = alle aktiven)</label>
|
||
<select id="zp-ordner">
|
||
<option value="">Alle aktiven Ordner</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group hidden" id="zp-regel-gruppe">
|
||
<label>Sortierregel (leer = alle aktiven)</label>
|
||
<select id="zp-regel">
|
||
<option value="">Alle aktiven Regeln</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Intervall</label>
|
||
<select id="zp-intervall" onchange="zeitplanIntervallChanged()">
|
||
<option value="stündlich">Stündlich (jede Stunde)</option>
|
||
<option value="täglich" selected>Täglich (einmal pro Tag)</option>
|
||
<option value="wöchentlich">Wöchentlich (einmal pro Woche)</option>
|
||
<option value="monatlich">Monatlich (einmal pro Monat)</option>
|
||
</select>
|
||
<small id="zp-intervall-info" style="color: #666; display: block; margin-top: 4px;"></small>
|
||
</div>
|
||
|
||
<div class="form-row" id="zp-zeit-gruppe">
|
||
<div class="form-group">
|
||
<label>Uhrzeit (Stunde)</label>
|
||
<input type="number" id="zp-stunde" value="6" min="0" max="23" style="width: 80px;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Minute</label>
|
||
<input type="number" id="zp-minute" value="0" min="0" max="59" style="width: 80px;">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group hidden" id="zp-wochentag-gruppe">
|
||
<label>Wochentag</label>
|
||
<select id="zp-wochentag">
|
||
<option value="0">Montag</option>
|
||
<option value="1">Dienstag</option>
|
||
<option value="2">Mittwoch</option>
|
||
<option value="3">Donnerstag</option>
|
||
<option value="4">Freitag</option>
|
||
<option value="5">Samstag</option>
|
||
<option value="6">Sonntag</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group hidden" id="zp-monatstag-gruppe">
|
||
<label>Tag im Monat</label>
|
||
<input type="number" id="zp-monatstag" value="1" min="1" max="28" style="width: 80px;">
|
||
<small>1-28 (für alle Monate gültig)</small>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn" onclick="schliesseModal('zeitplan-modal')">Abbrechen</button>
|
||
<button class="btn btn-primary" onclick="speichereZeitplan()">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Einstellungen -->
|
||
<div id="einstellungen-modal" class="modal hidden">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>⚙️ Einstellungen</h3>
|
||
<button class="modal-close" onclick="schliesseModal('einstellungen-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label>Farbschema</label>
|
||
<div class="theme-options">
|
||
<button class="theme-option" data-theme="dark" onclick="setzeTheme('dark')">
|
||
<span class="theme-preview dark"></span>
|
||
<span>Dunkel</span>
|
||
</button>
|
||
<button class="theme-option" data-theme="light" onclick="setzeTheme('light')">
|
||
<span class="theme-preview light"></span>
|
||
<span>Hell</span>
|
||
</button>
|
||
<button class="theme-option" data-theme="blue" onclick="setzeTheme('blue')">
|
||
<span class="theme-preview blue"></span>
|
||
<span>Blau</span>
|
||
</button>
|
||
<button class="theme-option" data-theme="green" onclick="setzeTheme('green')">
|
||
<span class="theme-preview green"></span>
|
||
<span>Grün</span>
|
||
</button>
|
||
<button class="theme-option" data-theme="breeze" onclick="setzeTheme('breeze')">
|
||
<span class="theme-preview breeze"></span>
|
||
<span>Breeze Dark</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-primary" onclick="schliesseModal('einstellungen-modal')">Schließen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Debug Log -->
|
||
<div id="log-modal" class="modal hidden">
|
||
<div class="modal-content modal-large">
|
||
<div class="modal-header">
|
||
<h3>📋 Debug-Log</h3>
|
||
<button class="modal-close" onclick="schliesseModal('log-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<div class="log-controls">
|
||
<button class="btn btn-sm" onclick="ladeLog()">🔄 Aktualisieren</button>
|
||
<button class="btn btn-sm" onclick="leereLog()">🗑️ Leeren</button>
|
||
<select id="log-filter" onchange="ladeLog()">
|
||
<option value="">Alle</option>
|
||
<option value="ERROR">Fehler</option>
|
||
<option value="WARNING">Warnungen</option>
|
||
<option value="INFO">Info</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div id="log-container" class="log-container">
|
||
<p class="empty-state">Lade Log...</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-primary" onclick="schliesseModal('log-modal')">Schließen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal: Regel-Assistent -->
|
||
<div id="assistent-modal" class="modal hidden">
|
||
<div class="modal-content modal-large">
|
||
<div class="modal-header">
|
||
<h3>🧙 Regel-Assistent</h3>
|
||
<button class="modal-close" onclick="schliesseModal('assistent-modal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p style="margin-bottom: 1rem; color: var(--text-secondary);">
|
||
Beantworte die Fragen und ich erstelle die Regel für dich automatisch.
|
||
</p>
|
||
|
||
<!-- Schritt 1: Erkennung -->
|
||
<div class="assistent-section">
|
||
<h4>1. Woran erkenne ich diese Dokumente?</h4>
|
||
<div class="form-group">
|
||
<label>Welche Wörter müssen im Dokument vorkommen? (Komma-getrennt)</label>
|
||
<input type="text" id="ass-keywords" placeholder="z.B. rechnung, sonepar">
|
||
<small>Tipp: Firmenname + Dokumenttyp (z.B. "telekom, rechnung")</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Schritt 2: Firma -->
|
||
<div class="assistent-section">
|
||
<h4>2. Von welcher Firma ist das Dokument?</h4>
|
||
<div class="form-group">
|
||
<label>Firmenname</label>
|
||
<input type="text" id="ass-firma" placeholder="z.B. Sonepar, Telekom, Amazon">
|
||
<small>Wird im Dateinamen verwendet</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Schritt 3: Welche Felder extrahieren? -->
|
||
<div class="assistent-section">
|
||
<h4>3. Was soll aus dem Dokument extrahiert werden?</h4>
|
||
|
||
<div class="assistent-feld">
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" id="ass-datum-aktiv" checked>
|
||
<strong>📅 Datum</strong>
|
||
</label>
|
||
<select id="ass-datum-typ">
|
||
<option value="auto">Automatisch erkennen</option>
|
||
<option value="rechnungsdatum">Nach "Rechnungsdatum" suchen</option>
|
||
<option value="datum">Nach "Datum" suchen</option>
|
||
<option value="beliebig">Erstes Datum im Text</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="assistent-feld">
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" id="ass-betrag-aktiv" checked>
|
||
<strong>💰 Betrag</strong>
|
||
</label>
|
||
<select id="ass-betrag-typ">
|
||
<option value="auto">Automatisch erkennen</option>
|
||
<option value="gesamtbetrag">Nach "Gesamtbetrag" suchen</option>
|
||
<option value="summe">Nach "Summe" suchen</option>
|
||
<option value="brutto">Nach "Brutto" suchen</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="assistent-feld">
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" id="ass-nummer-aktiv" checked>
|
||
<strong>🔢 Rechnungsnummer</strong>
|
||
</label>
|
||
<select id="ass-nummer-typ">
|
||
<option value="auto">Automatisch erkennen</option>
|
||
<option value="rechnungsnummer">Nach "Rechnungsnummer" suchen</option>
|
||
<option value="belegnr">Nach "Beleg-Nr" suchen</option>
|
||
<option value="invoice">Nach "Invoice" suchen</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Schritt 4: Dateiname -->
|
||
<div class="assistent-section">
|
||
<h4>4. Wie soll die Datei heißen?</h4>
|
||
<div class="form-group">
|
||
<label>Dateiname-Schema</label>
|
||
<select id="ass-schema">
|
||
<option value="{datum} - Rechnung - {firma} - {nummer} - {betrag} EUR.pdf">Datum - Rechnung - Firma - Nummer - Betrag EUR.pdf</option>
|
||
<option value="{datum} - {firma} - Rechnung {nummer}.pdf">Datum - Firma - Rechnung Nummer.pdf</option>
|
||
<option value="{firma} - {datum} - {nummer}.pdf">Firma - Datum - Nummer.pdf</option>
|
||
<option value="{datum} - {firma} - {betrag} EUR.pdf">Datum - Firma - Betrag EUR.pdf</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Unterordner (optional)</label>
|
||
<input type="text" id="ass-unterordner" placeholder="z.B. rechnungen/sonepar">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Vorschau -->
|
||
<div class="assistent-section" style="background: var(--bg); padding: 1rem; border-radius: var(--radius);">
|
||
<h4>📋 Vorschau</h4>
|
||
<div id="ass-vorschau" style="font-family: monospace; font-size: 0.85rem;">
|
||
<em>Fülle die Felder aus um eine Vorschau zu sehen</em>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn" onclick="schliesseModal('assistent-modal')">Abbrechen</button>
|
||
<button class="btn btn-primary" onclick="assistentUebernehmen()">✓ Übernehmen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Universelles Dialog-Modal -->
|
||
<div id="dialog-modal" class="modal hidden">
|
||
<div class="modal-content dialog-modal-content">
|
||
<div class="modal-header">
|
||
<h3 id="dialog-title">Hinweis</h3>
|
||
<button class="modal-close" onclick="dialogSchliessen(false)">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="dialog-icon" class="dialog-icon"></div>
|
||
<div id="dialog-message" class="dialog-message"></div>
|
||
</div>
|
||
<div class="modal-footer" id="dialog-footer">
|
||
<button class="btn" id="dialog-cancel-btn" onclick="dialogSchliessen(false)">Abbrechen</button>
|
||
<button class="btn btn-primary" id="dialog-ok-btn" onclick="dialogSchliessen(true)">OK</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Loading Overlay -->
|
||
<div id="loading-overlay" class="loading-overlay hidden">
|
||
<div class="spinner"></div>
|
||
<div class="loading-text" id="loading-text">Wird geladen...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/static/js/app.js"></script>
|
||
</body>
|
||
</html>
|