belege-import/app/templates/platforms.html

364 lines
15 KiB
HTML

{% extends "base.html" %}
{% set active_page = "platforms" %}
{% block content %}
{% if message %}
<div class="alert alert-{{ message_type or 'info' }}">{{ message }}</div>
{% endif %}
<div class="card">
<h2>Amazon Business - Einstellungen</h2>
<div class="form-grid">
<div class="form-group">
<label for="amazon_enabled">Status</label>
<select id="amazon_enabled" name="amazon_enabled">
<option value="false" {% if settings.get('amazon_enabled') != 'true' %}selected{% endif %}>Deaktiviert</option>
<option value="true" {% if settings.get('amazon_enabled') == 'true' %}selected{% endif %}>Aktiviert</option>
</select>
</div>
<div class="form-group">
<label for="amazon_domain">Amazon-Domain</label>
<select id="amazon_domain" name="amazon_domain">
<option value="amazon.de" {% if settings.get('amazon_domain') == 'amazon.de' %}selected{% endif %}>amazon.de</option>
<option value="amazon.at" {% if settings.get('amazon_domain') == 'amazon.at' %}selected{% endif %}>amazon.at</option>
<option value="amazon.com" {% if settings.get('amazon_domain') == 'amazon.com' %}selected{% endif %}>amazon.com</option>
</select>
</div>
<div class="form-group">
<label for="amazon_email">Amazon E-Mail</label>
<input type="email" id="amazon_email" name="amazon_email"
value="{{ settings.get('amazon_email', '') }}"
placeholder="email@example.com">
</div>
<div class="form-group">
<label for="amazon_password">Amazon Passwort</label>
<input type="password" id="amazon_password" name="amazon_password"
placeholder="{% if settings.get('amazon_password') %}(gespeichert){% else %}Passwort eingeben{% endif %}">
</div>
<div class="form-group">
<label for="amazon_since_date">Rechnungen ab Datum</label>
<input type="date" id="amazon_since_date" name="amazon_since_date"
value="{{ settings.get('amazon_since_date', '') }}">
<small class="text-muted">Leer = letzte 30 Tage</small>
</div>
<div class="form-group" style="align-self:end;">
{% if settings.get('amazon_last_sync') %}
<small class="text-muted">Letzter Abruf: {{ settings.get('amazon_last_sync') }}</small>
{% endif %}
</div>
</div>
<div class="form-actions" style="margin-top:1rem;">
<button type="button" class="btn btn-primary" onclick="saveAmazonSettings()">Einstellungen speichern</button>
</div>
<div id="settingsMsg" style="margin-top:0.75rem;"></div>
</div>
<div class="card">
<h2>Anmeldung &amp; Abruf</h2>
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:1rem;">
<span>Session:</span>
<span id="sessionBadge" class="badge badge-inactive">Wird geprüft...</span>
</div>
<div style="display:flex;gap:0.75rem;flex-wrap:wrap;">
<button type="button" id="btnLogin" class="btn btn-primary" onclick="doLogin()">Bei Amazon anmelden</button>
<button type="button" id="btnLogout" class="btn btn-secondary" onclick="doLogout()" style="display:none;">Session löschen</button>
<button type="button" id="btnProcess" class="btn btn-success" onclick="doProcess()" style="display:none;">Jetzt Rechnungen abrufen</button>
<button type="button" class="btn btn-secondary" onclick="doReset()">Importierte zurücksetzen</button>
</div>
<div id="processMsg" style="margin-top:0.75rem;"></div>
</div>
<!-- Interactive Browser Modal -->
<div id="browserModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:1000;overflow:auto;">
<div style="max-width:1340px;margin:2rem auto;background:var(--card-bg);border-radius:8px;padding:1.5rem;position:relative;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
<h3 style="margin:0;">Amazon Login - Interaktiver Browser</h3>
<button type="button" class="btn btn-secondary" onclick="closeBrowser()" style="padding:0.25rem 0.75rem;">Schließen</button>
</div>
<div id="browserInfo" class="alert alert-info" style="margin-bottom:1rem;">
Klicken und tippen Sie im Browser-Bild, um sich bei Amazon anzumelden. CAPTCHAs und 2FA können Sie direkt lösen.
</div>
<div style="position:relative;display:inline-block;border:1px solid var(--border-color);cursor:crosshair;">
<img id="browserImg" src="" alt="Browser" style="display:block;max-width:100%;height:auto;"
onclick="onBrowserClick(event)">
</div>
<div style="margin-top:1rem;display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
<input type="text" id="browserInput" placeholder="Text eingeben und Enter drücken..."
style="flex:1;min-width:200px;padding:0.5rem;border:1px solid var(--border-color);border-radius:4px;background:var(--input-bg);color:var(--text-color);"
onkeydown="onBrowserKeydown(event)">
<button type="button" class="btn btn-primary" onclick="sendBrowserText()">Senden</button>
<button type="button" class="btn btn-secondary" onclick="sendKey('Tab')">Tab</button>
<button type="button" class="btn btn-secondary" onclick="sendKey('Escape')">Esc</button>
<button type="button" class="btn btn-secondary" onclick="sendKey('Backspace')">&#9003;</button>
</div>
</div>
</div>
<script>
// --- Settings ---
async function saveAmazonSettings() {
const btn = event.target;
btn.disabled = true;
btn.textContent = 'Speichert...';
const msgEl = document.getElementById('settingsMsg');
const data = {
amazon_enabled: document.getElementById('amazon_enabled').value,
amazon_domain: document.getElementById('amazon_domain').value,
amazon_email: document.getElementById('amazon_email').value,
amazon_password: document.getElementById('amazon_password').value,
amazon_since_date: document.getElementById('amazon_since_date').value,
};
try {
const resp = await fetch('/api/amazon-settings', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data),
});
const result = await resp.json();
if (result.success) {
msgEl.innerHTML = '<div class="alert alert-success">Einstellungen gespeichert</div>';
if (data.amazon_password) {
document.getElementById('amazon_password').value = '';
document.getElementById('amazon_password').placeholder = '(gespeichert)';
}
} else {
msgEl.innerHTML = '<div class="alert alert-error">' + escapeHtml(result.error || 'Fehler') + '</div>';
}
} catch (e) {
msgEl.innerHTML = '<div class="alert alert-error">Verbindungsfehler</div>';
} finally {
btn.disabled = false;
btn.textContent = 'Einstellungen speichern';
}
}
// --- Session Status ---
async function checkSession() {
const badge = document.getElementById('sessionBadge');
try {
const resp = await fetch('/api/amazon-status');
const data = await resp.json();
if (data.login_active) {
badge.className = 'badge badge-warning';
badge.textContent = 'Login läuft...';
document.getElementById('btnLogout').style.display = 'none';
document.getElementById('btnProcess').style.display = 'none';
} else if (data.session_valid) {
badge.className = 'badge badge-success';
badge.textContent = 'Angemeldet';
document.getElementById('btnLogout').style.display = '';
document.getElementById('btnProcess').style.display = '';
} else {
badge.className = 'badge badge-inactive';
badge.textContent = 'Nicht angemeldet';
document.getElementById('btnLogout').style.display = 'none';
document.getElementById('btnProcess').style.display = 'none';
}
} catch (e) {
badge.className = 'badge badge-inactive';
badge.textContent = 'Unbekannt';
}
}
// --- Interactive Browser Login ---
let screenshotInterval = null;
let loginPollInterval = null;
async function doLogin() {
const msgEl = document.getElementById('processMsg');
msgEl.innerHTML = '<div class="alert alert-info">Browser wird gestartet...</div>';
try {
await fetch('/api/amazon-login', {method: 'POST'});
// Open browser modal
document.getElementById('browserModal').style.display = '';
msgEl.innerHTML = '';
startScreenshotPolling();
startLoginStatePolling();
} catch (e) {
msgEl.innerHTML = '<div class="alert alert-error">Browser konnte nicht gestartet werden</div>';
}
}
function startScreenshotPolling() {
if (screenshotInterval) clearInterval(screenshotInterval);
refreshScreenshot();
screenshotInterval = setInterval(refreshScreenshot, 1500);
}
async function refreshScreenshot() {
try {
const resp = await fetch('/api/amazon-browser-screenshot');
if (resp.ok) {
const blob = await resp.blob();
document.getElementById('browserImg').src = URL.createObjectURL(blob);
}
} catch (e) {}
}
function startLoginStatePolling() {
if (loginPollInterval) clearInterval(loginPollInterval);
loginPollInterval = setInterval(async () => {
try {
const resp = await fetch('/api/amazon-login-state');
const data = await resp.json();
const info = document.getElementById('browserInfo');
if (data.status === 'logged_in') {
info.className = 'alert alert-success';
info.textContent = 'Erfolgreich angemeldet! Sie können das Fenster schließen.';
} else if (data.status === 'login_failed') {
info.className = 'alert alert-error';
info.textContent = data.message || 'Login fehlgeschlagen';
} else if (data.status === 'interactive') {
info.className = 'alert alert-info';
info.textContent = data.message || 'Bitte im Browser anmelden...';
}
} catch (e) {}
}, 2000);
}
async function onBrowserClick(event) {
const img = event.target;
const rect = img.getBoundingClientRect();
// Scale click coordinates to actual browser viewport (1280x800)
const scaleX = 1280 / img.clientWidth;
const scaleY = 800 / img.clientHeight;
const x = Math.round((event.clientX - rect.left) * scaleX);
const y = Math.round((event.clientY - rect.top) * scaleY);
try {
await fetch('/api/amazon-browser-click', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({x, y}),
});
setTimeout(refreshScreenshot, 500);
} catch (e) {}
}
async function sendBrowserText() {
const input = document.getElementById('browserInput');
const text = input.value;
if (!text) return;
try {
await fetch('/api/amazon-browser-type', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({text}),
});
input.value = '';
setTimeout(refreshScreenshot, 500);
} catch (e) {}
}
async function sendKey(key) {
try {
await fetch('/api/amazon-browser-key', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({key}),
});
setTimeout(refreshScreenshot, 500);
} catch (e) {}
}
function onBrowserKeydown(event) {
if (event.key === 'Enter') {
event.preventDefault();
sendBrowserText();
// Also send Enter to the browser
sendKey('Enter');
}
}
async function closeBrowser() {
if (screenshotInterval) { clearInterval(screenshotInterval); screenshotInterval = null; }
if (loginPollInterval) { clearInterval(loginPollInterval); loginPollInterval = null; }
document.getElementById('browserModal').style.display = 'none';
try {
await fetch('/api/amazon-login-close', {method: 'POST'});
} catch (e) {}
checkSession();
}
// --- Logout ---
async function doLogout() {
if (!confirm('Amazon-Session wirklich löschen? Sie müssen sich danach neu anmelden.')) return;
const btn = document.getElementById('btnLogout');
btn.disabled = true;
try {
await fetch('/api/amazon-logout', {method: 'POST'});
document.getElementById('processMsg').innerHTML = '<div class="alert alert-info">Session gelöscht</div>';
} catch (e) {
document.getElementById('processMsg').innerHTML = '<div class="alert alert-error">Fehler</div>';
} finally {
btn.disabled = false;
checkSession();
}
}
// --- Process ---
async function doProcess() {
const btn = document.getElementById('btnProcess');
const msgEl = document.getElementById('processMsg');
btn.disabled = true;
btn.textContent = 'Rechnungen werden abgerufen...';
msgEl.innerHTML = '';
try {
const resp = await fetch('/api/amazon-process', {method: 'POST'});
const data = await resp.json();
if (data.error) {
msgEl.innerHTML = '<div class="alert alert-error">' + escapeHtml(data.error) + '</div>';
} else if (data.processed > 0 || data.errors > 0) {
let msg = data.processed + ' Rechnung(en) importiert';
if (data.skipped > 0) msg += ', ' + data.skipped + ' übersprungen';
if (data.errors > 0) msg += ', ' + data.errors + ' Fehler';
const cls = data.errors > 0 ? 'warning' : 'success';
msgEl.innerHTML = '<div class="alert alert-' + cls + '">' + escapeHtml(msg) + '</div>';
} else {
msgEl.innerHTML = '<div class="alert alert-info">Keine neuen Rechnungen gefunden</div>';
}
} catch (e) {
msgEl.innerHTML = '<div class="alert alert-error">Verbindungsfehler</div>';
} finally {
btn.disabled = false;
btn.textContent = 'Jetzt Rechnungen abrufen';
checkSession();
}
}
// --- Reset ---
async function doReset() {
if (!confirm('Alle als importiert markierten Rechnungen zurücksetzen? Beim nächsten Abruf werden alle Rechnungen erneut heruntergeladen und gesendet.')) return;
const msgEl = document.getElementById('processMsg');
try {
const resp = await fetch('/api/amazon-reset', {method: 'POST'});
const data = await resp.json();
if (data.success) {
msgEl.innerHTML = '<div class="alert alert-info">' + data.count + ' Bestellung(en) zurückgesetzt</div>';
} else {
msgEl.innerHTML = '<div class="alert alert-error">' + escapeHtml(data.error || 'Fehler') + '</div>';
}
} catch (e) {
msgEl.innerHTML = '<div class="alert alert-error">Verbindungsfehler</div>';
}
}
function escapeHtml(str) {
const d = document.createElement('div');
d.textContent = str;
return d.innerHTML;
}
// Initial check
checkSession();
</script>
{% endblock %}