daaa9bff5e
- admin.html: Komplette Tab-Navigation implementiert - admin.html: Alle Funktionen (Räume, Bereiche, Blockierung, SMTP, LLM) - admin_routes.py: SQL-Fehler behoben (table_id → table_ids) Admin-Oberfläche jetzt voll funktionsfähig
737 lines
29 KiB
HTML
737 lines
29 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Reservierung Admin</title>
|
||
<style>
|
||
:root {
|
||
--bg: #0f172a;
|
||
--card: #1e293b;
|
||
--text: #f1f5f9;
|
||
--text-muted: #94a3b8;
|
||
--accent: #00d4aa;
|
||
--accent-dark: #00b894;
|
||
--danger: #ef4444;
|
||
--warning: #f59e0b;
|
||
--success: #10b981;
|
||
}
|
||
|
||
[data-theme="purple"] { --accent: #8b5cf6; --accent-dark: #7c3aed; }
|
||
[data-theme="orange"] { --accent: #f97316; --accent-dark: #ea580c; }
|
||
[data-theme="blue"] { --accent: #3b82f6; --accent-dark: #2563eb; }
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.sidebar {
|
||
width: 260px;
|
||
background: var(--card);
|
||
position: fixed;
|
||
height: 100vh;
|
||
border-right: 1px solid #334155;
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.logo {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
color: var(--accent);
|
||
margin-bottom: 2rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.nav-item {
|
||
padding: 0.75rem 1rem;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
margin-bottom: 0.5rem;
|
||
color: var(--text-muted);
|
||
transition: all 0.2s;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.nav-item:hover, .nav-item.active {
|
||
background: var(--accent);
|
||
color: #000;
|
||
}
|
||
|
||
.main {
|
||
margin-left: 260px;
|
||
padding: 2rem;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 2rem;
|
||
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-dark) 100%);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
}
|
||
|
||
.card {
|
||
background: var(--card);
|
||
border-radius: 16px;
|
||
padding: 1.5rem;
|
||
margin-bottom: 1.5rem;
|
||
border: 1px solid #334155;
|
||
}
|
||
|
||
.card-title {
|
||
font-size: 1.25rem;
|
||
font-weight: 600;
|
||
margin-bottom: 1rem;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.btn {
|
||
padding: 0.625rem 1.25rem;
|
||
border-radius: 8px;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: var(--accent);
|
||
color: #000;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 212, 170, 0.4);
|
||
}
|
||
|
||
.btn-danger {
|
||
background: var(--danger);
|
||
color: #fff;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 0.5rem;
|
||
color: var(--text-muted);
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.form-group input, .form-group select, .form-group textarea {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
border: 1px solid #334155;
|
||
border-radius: 8px;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
}
|
||
|
||
.theme-selector {
|
||
display: flex;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.theme-btn {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
border: 3px solid transparent;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.theme-btn.active { border-color: #fff; }
|
||
.theme-green { background: linear-gradient(135deg, #00d4aa, #00b894); }
|
||
.theme-purple { background: linear-gradient(135deg, #8b5cf6, #7c3aed); }
|
||
.theme-orange { background: linear-gradient(135deg, #f97316, #ea580c); }
|
||
.theme-blue { background: linear-gradient(135deg, #3b82f6, #2563eb); }
|
||
|
||
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
|
||
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
|
||
|
||
.stat-box {
|
||
background: var(--bg);
|
||
padding: 1.5rem;
|
||
border-radius: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-box h3 {
|
||
font-size: 2.5rem;
|
||
color: var(--accent);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.stat-box p {
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.tab-content { display: none; }
|
||
.tab-content.active { display: block; }
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
th, td {
|
||
padding: 0.75rem;
|
||
text-align: left;
|
||
border-bottom: 1px solid #334155;
|
||
}
|
||
|
||
th {
|
||
color: var(--text-muted);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.hidden { display: none !important; }
|
||
|
||
@media (max-width: 1024px) {
|
||
.sidebar { width: 100%; position: relative; height: auto; }
|
||
.main { margin-left: 0; }
|
||
.grid-2, .grid-3 { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body data-theme="green">
|
||
<aside class="sidebar">
|
||
<div class="logo">🍽️ Admin</div>
|
||
<nav>
|
||
<div class="nav-item active" onclick="showTab('dashboard', this)">📊 Dashboard</div>
|
||
<div class="nav-item" onclick="showTab('rooms', this)">🏛️ Räume & Bereiche</div>
|
||
<div class="nav-item" onclick="showTab('smtp', this)">📧 E-Mail (SMTP)</div>
|
||
<div class="nav-item" onclick="showTab('llm', this)">🤖 LLM (Ollama)</div>
|
||
<div class="nav-item" onclick="showTab('settings', this)">⚙️ Einstellungen</div>
|
||
<div class="nav-item" onclick="logout()">🚪 Abmelden</div>
|
||
</nav>
|
||
</aside>
|
||
|
||
<main class="main">
|
||
<div class="header">
|
||
<h1 id="page-title">Dashboard</h1>
|
||
<div class="theme-selector">
|
||
<div class="theme-btn theme-green active" onclick="setTheme('green')"></div>
|
||
<div class="theme-btn theme-purple" onclick="setTheme('purple')"></div>
|
||
<div class="theme-btn theme-orange" onclick="setTheme('orange')"></div>
|
||
<div class="theme-btn theme-blue" onclick="setTheme('blue')"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="content">
|
||
<!-- DASHBOARD TAB -->
|
||
<div id="tab-dashboard" class="tab-content active">
|
||
<div class="card">
|
||
<div class="card-title">📊 Heutige Übersicht</div>
|
||
<div class="grid-3">
|
||
<div class="stat-box">
|
||
<h3 id="stat-today-count">-</h3>
|
||
<p>Reservierungen heute</p>
|
||
</div>
|
||
<div class="stat-box">
|
||
<h3 id="stat-today-guests">-</h3>
|
||
<p>Gäste heute</p>
|
||
</div>
|
||
<div class="stat-box">
|
||
<h3 id="stat-pending">-</h3>
|
||
<p>Ausstehende E-Mails</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-title">📋 Heutige Reservierungen</div>
|
||
<div id="today-reservations">Lädt...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ROOMS TAB -->
|
||
<div id="tab-rooms" class="tab-content">
|
||
<div class="card">
|
||
<div class="card-title">➕ Neuen Raum erstellen</div>
|
||
<div class="grid-2">
|
||
<div class="form-group">
|
||
<label>Raum Name</label>
|
||
<input type="text" id="roomName" placeholder="z.B. Hauptraum">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Kapazität</label>
|
||
<input type="number" id="roomCapacity" value="50">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Farbe</label>
|
||
<input type="color" id="roomColor" value="#3498db">
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="createRoom()">💾 Raum erstellen</button>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-title">➕ Neuen Bereich erstellen</div>
|
||
<div class="grid-2">
|
||
<div class="form-group">
|
||
<label>Bereich Name</label>
|
||
<input type="text" id="areaName" placeholder="z.B. Fensterbereich">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Raum</label>
|
||
<select id="areaRoom"></select>
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="createArea()">💾 Bereich erstellen</button>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-title">🏛️ Räume & Bereiche</div>
|
||
<div id="rooms-list">Lädt...</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-title">🚫 Zeit blockieren</div>
|
||
<div class="grid-2">
|
||
<div class="form-group">
|
||
<label>Datum</label>
|
||
<input type="date" id="blockDate">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Raum</label>
|
||
<select id="blockRoom"></select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Von</label>
|
||
<input type="time" id="blockFrom" value="18:00">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Bis</label>
|
||
<input type="time" id="blockTo" value="23:00">
|
||
</div>
|
||
<div class="form-group" style="grid-column: span 2;">
|
||
<label>Grund</label>
|
||
<input type="text" id="blockReason" placeholder="z.B. Private Feier">
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="blockTime()">🚫 Zeit blockieren</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SMTP TAB -->
|
||
<div id="tab-smtp" class="tab-content">
|
||
<div class="card">
|
||
<div class="card-title">📧 SMTP Konfiguration</div>
|
||
<div class="grid-2">
|
||
<div class="form-group">
|
||
<label>SMTP Server</label>
|
||
<input type="text" id="smtpHost" placeholder="smtp.gmail.com">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Port</label>
|
||
<input type="number" id="smtpPort" value="587">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Benutzername</label>
|
||
<input type="text" id="smtpUser" placeholder="email@domain.de">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Passwort</label>
|
||
<input type="password" id="smtpPass" placeholder="••••••••">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Absender E-Mail</label>
|
||
<input type="email" id="smtpFrom" placeholder="reservierung@domain.de">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Absender Name</label>
|
||
<input type="text" id="smtpFromName" placeholder="Reservierungssystem">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Sicherheit</label>
|
||
<select id="smtpSecurity">
|
||
<option value="tls">TLS</option>
|
||
<option value="ssl">SSL</option>
|
||
<option value="none">Keine</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; gap: 0.5rem;">
|
||
<input type="checkbox" id="smtpEnabled"> SMTP aktiviert
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="saveSMTP()">💾 Speichern</button>
|
||
<button class="btn btn-primary" onclick="testSMTP()" style="margin-left: 0.5rem;">🧪 Testen</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- LLM TAB -->
|
||
<div id="tab-llm" class="tab-content">
|
||
<div class="card">
|
||
<div class="card-title">🤖 LLM Konfiguration (Ollama)</div>
|
||
<div class="grid-2">
|
||
<div class="form-group">
|
||
<label>Ollama URL</label>
|
||
<input type="text" id="llmUrl" value="http://localhost:11434">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Modell</label>
|
||
<select id="llmModel">
|
||
<option value="llama2">Llama 2</option>
|
||
<option value="mistral">Mistral</option>
|
||
<option value="mixtral">Mixtral</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Temperatur (0.0 - 1.0)</label>
|
||
<input type="number" id="llmTemp" value="0.7" min="0" max="1" step="0.1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Max Tokens</label>
|
||
<input type="number" id="llmTokens" value="500" min="100" max="2000" step="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; gap: 0.5rem;">
|
||
<input type="checkbox" id="llmEnabled"> LLM aktiviert
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; gap: 0.5rem;">
|
||
<input type="checkbox" id="llmAutoReply"> Auto-Antworten
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="saveLLM()">💾 Speichern</button>
|
||
<button class="btn btn-primary" onclick="testLLM()" style="margin-left: 0.5rem;">🧪 Testen</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SETTINGS TAB -->
|
||
<div id="tab-settings" class="tab-content">
|
||
<div class="card">
|
||
<div class="card-title">⚙️ Öffnungszeiten</div>
|
||
<div class="grid-2">
|
||
<div class="form-group">
|
||
<label>Öffnet um</label>
|
||
<input type="number" id="openHour" value="10" min="0" max="23">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Schließt um</label>
|
||
<input type="number" id="closeHour" value="23" min="0" max="23">
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="saveHours()">💾 Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<script>
|
||
// === THEME ===
|
||
function setTheme(theme) {
|
||
document.body.setAttribute('data-theme', theme);
|
||
document.querySelectorAll('.theme-btn').forEach(btn => btn.classList.remove('active'));
|
||
document.querySelector(`.theme-${theme}`).classList.add('active');
|
||
localStorage.setItem('theme', theme);
|
||
}
|
||
|
||
// === TAB SWITCHING ===
|
||
function showTab(tabName, element) {
|
||
// Nav aktualisieren
|
||
document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active'));
|
||
element.classList.add('active');
|
||
|
||
// Content aktualisieren
|
||
document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
|
||
document.getElementById(`tab-${tabName}`).classList.add('active');
|
||
|
||
// Titel aktualisieren
|
||
const titles = {
|
||
'dashboard': 'Dashboard',
|
||
'rooms': 'Räume & Bereiche',
|
||
'smtp': 'E-Mail Konfiguration',
|
||
'llm': 'LLM Konfiguration',
|
||
'settings': 'Einstellungen'
|
||
};
|
||
document.getElementById('page-title').textContent = titles[tabName] || 'Admin';
|
||
|
||
// Tab-spezifische Daten laden
|
||
if (tabName === 'dashboard') loadDashboard();
|
||
if (tabName === 'rooms') loadRooms();
|
||
if (tabName === 'smtp') loadSMTP();
|
||
if (tabName === 'llm') loadLLM();
|
||
}
|
||
|
||
// === AUTH ===
|
||
function logout() {
|
||
fetch('/api/admin/logout', {method: 'POST'}).then(() => location.href = '/');
|
||
}
|
||
|
||
// === DASHBOARD ===
|
||
async function loadDashboard() {
|
||
try {
|
||
const response = await fetch('/api/dashboard');
|
||
const data = await response.json();
|
||
|
||
document.getElementById('stat-today-count').textContent = data.today_count || 0;
|
||
document.getElementById('stat-today-guests').textContent = data.guests_today || 0;
|
||
document.getElementById('stat-pending').textContent = data.pending_emails || 0;
|
||
|
||
// Reservierungen anzeigen
|
||
const container = document.getElementById('today-reservations');
|
||
if (data.today_reservations && data.today_reservations.length > 0) {
|
||
let html = '<table><tr><th>Zeit</th><th>Name</th><th>Gäste</th><th>Status</th></tr>';
|
||
data.today_reservations.forEach(r => {
|
||
html += `<tr><td>${r.time_from}</td><td>${r.guest_name || 'Unbekannt'}</td><td>${r.guests}</td><td>${r.status}</td></tr>`;
|
||
});
|
||
html += '</table>';
|
||
container.innerHTML = html;
|
||
} else {
|
||
container.innerHTML = '<p style="color: var(--text-muted)">Keine Reservierungen heute</p>';
|
||
}
|
||
} catch (e) {
|
||
console.error('Dashboard load error:', e);
|
||
}
|
||
}
|
||
|
||
// === ROOMS ===
|
||
async function loadRooms() {
|
||
try {
|
||
const response = await fetch('/api/rooms');
|
||
const rooms = await response.json();
|
||
|
||
// Dropdowns befüllen
|
||
const roomSelects = ['areaRoom', 'blockRoom'];
|
||
roomSelects.forEach(id => {
|
||
const select = document.getElementById(id);
|
||
if (select) {
|
||
select.innerHTML = rooms.map(r => `<option value="${r.id}">${r.name}</option>`).join('');
|
||
}
|
||
});
|
||
|
||
// Räume anzeigen
|
||
const container = document.getElementById('rooms-list');
|
||
let html = '';
|
||
rooms.forEach(room => {
|
||
html += `<div style="margin-bottom: 1rem; padding: 1rem; background: var(--bg); border-radius: 8px;">`;
|
||
html += `<h4 style="color: var(--accent);">${room.name}</h4>`;
|
||
if (room.areas && room.areas.length > 0) {
|
||
html += '<ul>';
|
||
room.areas.forEach(area => {
|
||
html += `<li>${area.name} (${area.tables ? area.tables.length : 0} Tische)</li>`;
|
||
});
|
||
html += '</ul>';
|
||
}
|
||
html += '</div>';
|
||
});
|
||
container.innerHTML = html;
|
||
} catch (e) {
|
||
console.error('Rooms load error:', e);
|
||
}
|
||
}
|
||
|
||
async function createRoom() {
|
||
const data = {
|
||
name: document.getElementById('roomName').value,
|
||
capacity: parseInt(document.getElementById('roomCapacity').value),
|
||
color: document.getElementById('roomColor').value
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/rooms', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(data)
|
||
});
|
||
if (response.ok) {
|
||
alert('Raum erstellt!');
|
||
loadRooms();
|
||
} else {
|
||
alert('Fehler beim Erstellen');
|
||
}
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
async function createArea() {
|
||
const data = {
|
||
room_id: parseInt(document.getElementById('areaRoom').value),
|
||
name: document.getElementById('areaName').value
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/areas', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(data)
|
||
});
|
||
if (response.ok) {
|
||
alert('Bereich erstellt!');
|
||
loadRooms();
|
||
} else {
|
||
alert('Fehler beim Erstellen');
|
||
}
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
async function blockTime() {
|
||
const data = {
|
||
room_id: parseInt(document.getElementById('blockRoom').value),
|
||
date: document.getElementById('blockDate').value,
|
||
time_from: document.getElementById('blockFrom').value,
|
||
time_to: document.getElementById('blockTo').value,
|
||
reason: document.getElementById('blockReason').value
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/blocked-times', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(data)
|
||
});
|
||
if (response.ok) {
|
||
alert('Zeit blockiert!');
|
||
} else {
|
||
alert('Fehler beim Blockieren');
|
||
}
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
// === SMTP ===
|
||
async function loadSMTP() {
|
||
try {
|
||
const response = await fetch('/api/admin/smtp');
|
||
const data = await response.json();
|
||
|
||
document.getElementById('smtpHost').value = data.host || '';
|
||
document.getElementById('smtpPort').value = data.port || 587;
|
||
document.getElementById('smtpUser').value = data.user || '';
|
||
document.getElementById('smtpFrom').value = data.from_email || '';
|
||
document.getElementById('smtpFromName').value = data.from_name || '';
|
||
document.getElementById('smtpSecurity').value = data.security || 'tls';
|
||
document.getElementById('smtpEnabled').checked = data.enabled || false;
|
||
} catch (e) {
|
||
console.error('SMTP load error:', e);
|
||
}
|
||
}
|
||
|
||
async function saveSMTP() {
|
||
const data = {
|
||
host: document.getElementById('smtpHost').value,
|
||
port: parseInt(document.getElementById('smtpPort').value),
|
||
user: document.getElementById('smtpUser').value,
|
||
password: document.getElementById('smtpPass').value,
|
||
from_email: document.getElementById('smtpFrom').value,
|
||
from_name: document.getElementById('smtpFromName').value,
|
||
security: document.getElementById('smtpSecurity').value,
|
||
enabled: document.getElementById('smtpEnabled').checked
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/smtp', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(data)
|
||
});
|
||
if (response.ok) {
|
||
alert('SMTP gespeichert!');
|
||
} else {
|
||
alert('Fehler beim Speichern');
|
||
}
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
async function testSMTP() {
|
||
try {
|
||
const response = await fetch('/api/admin/smtp/test', {method: 'POST'});
|
||
const data = await response.json();
|
||
alert(data.message || data.error);
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
// === LLM ===
|
||
async function loadLLM() {
|
||
try {
|
||
const response = await fetch('/api/admin/llm-config');
|
||
const data = await response.json();
|
||
|
||
document.getElementById('llmUrl').value = data.url || 'http://localhost:11434';
|
||
document.getElementById('llmModel').value = data.model || 'llama2';
|
||
document.getElementById('llmTemp').value = data.temperature || 0.7;
|
||
document.getElementById('llmTokens').value = data.max_tokens || 500;
|
||
document.getElementById('llmEnabled').checked = data.enabled || false;
|
||
document.getElementById('llmAutoReply').checked = data.auto_reply || false;
|
||
} catch (e) {
|
||
console.error('LLM load error:', e);
|
||
}
|
||
}
|
||
|
||
async function saveLLM() {
|
||
const data = {
|
||
url: document.getElementById('llmUrl').value,
|
||
model: document.getElementById('llmModel').value,
|
||
temperature: parseFloat(document.getElementById('llmTemp').value),
|
||
max_tokens: parseInt(document.getElementById('llmTokens').value),
|
||
enabled: document.getElementById('llmEnabled').checked,
|
||
auto_reply: document.getElementById('llmAutoReply').checked
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/llm-config', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(data)
|
||
});
|
||
if (response.ok) {
|
||
alert('LLM gespeichert!');
|
||
} else {
|
||
alert('Fehler beim Speichern');
|
||
}
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
async function testLLM() {
|
||
try {
|
||
const response = await fetch('/api/admin/llm-config/test', {method: 'POST'});
|
||
const data = await response.json();
|
||
alert(data.message || data.error);
|
||
} catch (e) {
|
||
alert('Fehler: ' + e.message);
|
||
}
|
||
}
|
||
|
||
// === INIT ===
|
||
const savedTheme = localStorage.getItem('theme') || 'green';
|
||
setTheme(savedTheme);
|
||
loadDashboard();
|
||
|
||
// Datum auf heute setzen
|
||
document.getElementById('blockDate').value = new Date().toISOString().split('T')[0];
|
||
</script>
|
||
</body>
|
||
</html> |