374 lines
24 KiB
PHP
374 lines
24 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||
require_once __DIR__ . '/../includes/assistant.php';
|
||
$admin = require_admin_or_permission('manage_assistant');
|
||
$pdo = get_db();
|
||
|
||
$errors = [];
|
||
$testQuestion = '';
|
||
$testResult = null;
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
$action = $_POST['action'] ?? '';
|
||
|
||
if ($action === 'save_settings') {
|
||
$dept = (int) ($_POST['assistant_department_id'] ?? 0);
|
||
$prio = (int) ($_POST['assistant_priority_id'] ?? 0);
|
||
$mode = ($_POST['assistant_mode'] ?? 'offline_only') === 'always' ? 'always' : 'offline_only';
|
||
if ($dept > 0) { $c = $pdo->prepare('SELECT COUNT(*) FROM departments WHERE id = ? AND is_active = 1'); $c->execute([$dept]); if (!(int) $c->fetchColumn()) $dept = 0; }
|
||
if ($prio > 0) { $c = $pdo->prepare('SELECT COUNT(*) FROM priorities WHERE id = ?'); $c->execute([$prio]); if (!(int) $c->fetchColumn()) $prio = 0; }
|
||
set_setting('assistant_enabled', !empty($_POST['assistant_enabled']) ? '1' : '0');
|
||
set_setting('assistant_mode', $mode);
|
||
set_setting('assistant_name', mb_substr(trim(strip_tags((string) ($_POST['assistant_name'] ?? ''))), 0, 60));
|
||
set_setting('assistant_greeting', mb_substr(trim(strip_tags((string) ($_POST['assistant_greeting'] ?? ''))), 0, 500));
|
||
set_setting('assistant_no_answer', mb_substr(trim(strip_tags((string) ($_POST['assistant_no_answer'] ?? ''))), 0, 500));
|
||
set_setting('assistant_department_id', (string) $dept);
|
||
set_setting('assistant_priority_id', (string) $prio);
|
||
set_setting('assistant_log_days', (string) max(0, min(3650, (int) ($_POST['assistant_log_days'] ?? 90))));
|
||
audit_log($admin['id'], 'Einstellungen des digitalen Assistenten geändert');
|
||
flash('success', 'Einstellungen gespeichert.');
|
||
redirect('/staff/assistant.php');
|
||
}
|
||
|
||
if ($action === 'save_answer') {
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$title = trim(mb_substr((string) ($_POST['title'] ?? ''), 0, 255));
|
||
$keywords = trim(mb_substr((string) ($_POST['keywords'] ?? ''), 0, 1000));
|
||
$answer = trim(mb_substr((string) ($_POST['answer'] ?? ''), 0, 4000));
|
||
$active = !empty($_POST['is_active']) ? 1 : 0;
|
||
if ($title === '' || $answer === '') {
|
||
$errors[] = 'Bitte eine Frage (Titel) und die Antwort angeben.';
|
||
} else {
|
||
$now = db_now();
|
||
if ($id > 0) {
|
||
$pdo->prepare('UPDATE assistant_answers SET title=?, keywords=?, answer=?, is_active=?, updated_at=? WHERE id=?')->execute([$title, $keywords ?: null, $answer, $active, $now, $id]);
|
||
audit_log($admin['id'], "Antwort des digitalen Assistenten geändert: {$title}");
|
||
flash('success', 'Antwort gespeichert.');
|
||
} else {
|
||
$pdo->prepare('INSERT INTO assistant_answers (title, keywords, answer, is_active, created_at, updated_at) VALUES (?,?,?,?,?,?)')->execute([$title, $keywords ?: null, $answer, $active, $now, $now]);
|
||
audit_log($admin['id'], "Antwort des digitalen Assistenten angelegt: {$title}");
|
||
flash('success', 'Antwort angelegt.');
|
||
}
|
||
redirect('/staff/assistant.php?tab=answers');
|
||
}
|
||
}
|
||
|
||
if ($action === 'delete_answer') {
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$t = $pdo->prepare('SELECT title FROM assistant_answers WHERE id = ?');
|
||
$t->execute([$id]);
|
||
$title = $t->fetchColumn();
|
||
if ($title !== false) {
|
||
$pdo->prepare('DELETE FROM assistant_answers WHERE id = ?')->execute([$id]);
|
||
audit_log($admin['id'], "Antwort des digitalen Assistenten gelöscht: {$title}");
|
||
flash('success', 'Antwort gelöscht.');
|
||
}
|
||
redirect('/staff/assistant.php?tab=answers');
|
||
}
|
||
|
||
if ($action === 'save_synonyms') {
|
||
$text = mb_substr(trim(strip_tags((string) ($_POST['assistant_synonyms'] ?? ''))), 0, 5000);
|
||
set_setting('assistant_synonyms', $text);
|
||
audit_log($admin['id'], 'Gleichbedeutende Wörter des digitalen Assistenten geändert');
|
||
flash('success', 'Gleichbedeutende Wörter gespeichert (' . count(assistant_custom_synonyms($text)) . ' Gruppen).');
|
||
redirect('/staff/assistant.php');
|
||
}
|
||
|
||
if ($action === 'clear_log') {
|
||
try {
|
||
$n = $pdo->exec('DELETE FROM assistant_log');
|
||
audit_log($admin['id'], 'Fragen-Log des digitalen Assistenten geleert');
|
||
flash('success', 'Fragen-Log geleert (' . (int) $n . ' Einträge).');
|
||
} catch (Throwable $e) {
|
||
flash('error', 'Das Log konnte nicht geleert werden. Bitte die Datenbank aktualisieren.');
|
||
}
|
||
redirect('/staff/assistant.php?tab=log');
|
||
}
|
||
|
||
if ($action === 'test') {
|
||
$testQuestion = trim(mb_substr((string) ($_POST['question'] ?? ''), 0, 300));
|
||
$testResult = $testQuestion !== '' ? (assistant_search($pdo, $testQuestion, false) ?: false) : null;
|
||
}
|
||
}
|
||
|
||
$answers = [];
|
||
$dbOutdated = false;
|
||
try {
|
||
$answers = $pdo->query('SELECT * FROM assistant_answers ORDER BY title')->fetchAll();
|
||
} catch (Throwable $e) {
|
||
$dbOutdated = true;
|
||
}
|
||
$edit = null;
|
||
if (!empty($_GET['edit'])) {
|
||
foreach ($answers as $a) if ((int) $a['id'] === (int) $_GET['edit']) $edit = $a;
|
||
}
|
||
$showForm = isset($_GET['new']) || $edit || $errors;
|
||
$tab = in_array($_GET['tab'] ?? '', ['settings', 'answers', 'log'], true) ? $_GET['tab'] : 'settings';
|
||
if (isset($_GET['edit']) || isset($_GET['new']) || $errors || ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_answer')) $tab = 'answers';
|
||
if (isset($_GET['lr']) || isset($_GET['lp']) || isset($_GET['lq']) || isset($_GET['lpage'])) $tab = 'log';
|
||
$prefillTitle = (!$edit && empty($_POST) && isset($_GET['title'])) ? mb_substr(trim((string) $_GET['title']), 0, 255) : '';
|
||
|
||
$logFilter = [
|
||
'result' => (string) ($_GET['lr'] ?? ''),
|
||
'period' => in_array($_GET['lp'] ?? '', ['today', '7', '30', 'all'], true) ? $_GET['lp'] : '30',
|
||
'q' => trim((string) ($_GET['lq'] ?? '')),
|
||
];
|
||
$logResults = assistant_log_filters();
|
||
$logPage = ['rows' => [], 'total' => 0, 'pages' => 1, 'page' => 1];
|
||
$logStats = array_fill_keys(array_keys($logResults), 0);
|
||
$logTop = [];
|
||
$logOutdated = false;
|
||
try {
|
||
$logPage = assistant_log_list($pdo, $logFilter, (int) ($_GET['lpage'] ?? 1));
|
||
$logStats = assistant_log_stats($pdo, $logFilter['period']);
|
||
$logTop = assistant_log_unanswered($pdo, $logFilter['period']);
|
||
} catch (Throwable $e) {
|
||
$logOutdated = true;
|
||
}
|
||
$logQuery = static fn(array $over = []) => '/staff/assistant.php?' . http_build_query(array_filter(array_merge(['tab' => 'log', 'lr' => $logFilter['result'], 'lp' => $logFilter['period'], 'lq' => $logFilter['q']], $over), static fn($v) => $v !== '' && $v !== null));
|
||
$departments = $pdo->query('SELECT id, name FROM departments WHERE is_active = 1 ORDER BY name')->fetchAll();
|
||
$priorities = $pdo->query('SELECT id, name FROM priorities ORDER BY sort_order')->fetchAll();
|
||
$texts = assistant_texts();
|
||
$enabled = assistant_enabled();
|
||
$activeNow = assistant_active($pdo);
|
||
$liveChatOn = get_setting('live_chat_enabled', '0') === '1';
|
||
$kbOn = get_setting('kb_enabled', '1') === '1';
|
||
|
||
$pageTitle = 'Digitaler Assistent';
|
||
$activePage = 'assistant';
|
||
require __DIR__ . '/_header.php';
|
||
?>
|
||
|
||
<h1>Digitaler Assistent</h1>
|
||
|
||
<?php foreach ($errors as $err): ?><div class="flash error"><?= e($err) ?></div><?php endforeach; ?>
|
||
<?php if ($dbOutdated): ?>
|
||
<div class="flash warning">Die Datenbank ist noch nicht auf dem neuesten Stand. Bitte unter System-Update auf „Datenbank aktualisieren“ klicken.</div>
|
||
<?php endif; ?>
|
||
|
||
<?php
|
||
$tabs = ['settings' => 'Einstellungen', 'answers' => 'Zusätzliche Antworten' . ($answers ? ' (' . count($answers) . ')' : ''), 'log' => 'Fragen-Log'];
|
||
?>
|
||
<div style="display:flex;gap:4px;flex-wrap:wrap;border-bottom:1px solid var(--border);margin-bottom:20px">
|
||
<?php foreach ($tabs as $key => $label): ?>
|
||
<a href="/staff/assistant.php?tab=<?= e($key) ?>" style="padding:9px 16px;text-decoration:none;color:inherit;border-bottom:2px solid <?= $tab === $key ? 'var(--primary,#2563eb)' : 'transparent' ?>;font-weight:<?= $tab === $key ? '600' : '400' ?>;margin-bottom:-1px"><?= e($label) ?></a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<?php if ($tab === 'settings'): ?>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(380px,1fr));gap:20px;align-items:start">
|
||
|
||
<div class="card">
|
||
<h3>Einstellungen</h3>
|
||
<p class="muted" style="font-size:.85rem;margin-bottom:12px">
|
||
Der Assistent beantwortet Fragen von Kunden aus der Wissensdatenbank und aus den Antworten unten. Findet er nichts, fragt er, ob er ein Ticket
|
||
erstellen soll, und erfragt dafür alles Nötige. Es wird kein externer KI-Dienst verwendet, Fragen bleiben auf diesem Server.
|
||
</p>
|
||
<p style="margin:0 0 12px;font-size:.88rem">
|
||
Zustand jetzt:
|
||
<?php if (!$enabled): ?><strong style="color:#b45309">ausgeschaltet</strong>
|
||
<?php elseif ($activeNow): ?><strong style="color:#15803d">aktiv</strong> <span class="muted">(Kunden sehen den Assistenten)</span>
|
||
<?php else: ?><strong style="color:#64748b">bereit, aber gerade nicht sichtbar</strong> <span class="muted">(Live-Chat ist besetzt<?= get_setting('helpdesk_status', 'online') === 'offline' ? ' bzw. das Supportcenter ist im Wartungsmodus' : '' ?>)</span><?php endif; ?>
|
||
</p>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="save_settings">
|
||
<label style="display:flex;align-items:center;gap:8px;margin:0"><input type="checkbox" name="assistant_enabled" value="1" style="width:auto" <?= $enabled ? 'checked' : '' ?>> Digitalen Assistenten einschalten</label>
|
||
<label for="assistant_mode" style="margin-top:12px">Wann ist er aktiv?</label>
|
||
<select id="assistant_mode" name="assistant_mode">
|
||
<option value="offline_only" <?= get_setting('assistant_mode', 'offline_only') !== 'always' ? 'selected' : '' ?>>Nur wenn der Live-Chat ausgeschaltet oder niemand erreichbar ist (Standard)</option>
|
||
<option value="always" <?= get_setting('assistant_mode', 'offline_only') === 'always' ? 'selected' : '' ?>>Immer (zum Testen)</option>
|
||
</select>
|
||
<p class="muted" style="font-size:.78rem;margin-top:4px">Live-Chat ist derzeit <strong><?= $liveChatOn ? 'eingeschaltet' : 'ausgeschaltet' ?></strong> (Einstellungen → Allgemein). Wissensdatenbank im Kundenportal: <strong><?= $kbOn ? 'an' : 'aus (dann nutzt der Assistent nur die Antworten unten)' ?></strong>.</p>
|
||
<label for="assistant_name" style="margin-top:12px">Name des Assistenten</label>
|
||
<input type="text" id="assistant_name" name="assistant_name" maxlength="60" value="<?= e(get_setting('assistant_name', '')) ?>" placeholder="Digitaler Assistent">
|
||
<label for="assistant_greeting" style="margin-top:12px">Begrüßung</label>
|
||
<textarea id="assistant_greeting" name="assistant_greeting" rows="3" maxlength="500" placeholder="<?= e($texts['greeting']) ?>"><?= e(get_setting('assistant_greeting', '')) ?></textarea>
|
||
<label for="assistant_no_answer" style="margin-top:12px">Text, wenn er keine Antwort findet</label>
|
||
<textarea id="assistant_no_answer" name="assistant_no_answer" rows="2" maxlength="500" placeholder="<?= e($texts['no_answer']) ?>"><?= e(get_setting('assistant_no_answer', '')) ?></textarea>
|
||
<label for="assistant_department_id" style="margin-top:12px">Abteilung für Tickets des Assistenten</label>
|
||
<select id="assistant_department_id" name="assistant_department_id">
|
||
<option value="0">— Standardabteilung (bzw. Abteilung des gewählten Themas) —</option>
|
||
<?php foreach ($departments as $d): ?><option value="<?= (int) $d['id'] ?>" <?= (int) get_setting('assistant_department_id', '0') === (int) $d['id'] ? 'selected' : '' ?>><?= e($d['name']) ?></option><?php endforeach; ?>
|
||
</select>
|
||
<label for="assistant_priority_id" style="margin-top:12px">Priorität für Tickets des Assistenten</label>
|
||
<select id="assistant_priority_id" name="assistant_priority_id">
|
||
<option value="0">— Standard —</option>
|
||
<?php foreach ($priorities as $p): ?><option value="<?= (int) $p['id'] ?>" <?= (int) get_setting('assistant_priority_id', '0') === (int) $p['id'] ? 'selected' : '' ?>><?= e($p['name']) ?></option><?php endforeach; ?>
|
||
</select>
|
||
<label for="assistant_log_days" style="margin-top:12px">Fragen-Log: Aufbewahrung in Tagen (0 = nichts speichern)</label>
|
||
<input type="number" id="assistant_log_days" name="assistant_log_days" min="0" max="3650" value="<?= (int) assistant_log_days() ?>" style="max-width:140px">
|
||
<p class="muted" style="font-size:.78rem;margin-top:4px">Die Tickets tragen den Tag „Assistent“ und durchlaufen deine Ticket-Regeln und die automatische Zuweisung der Abteilung. Wählt der Kunde ein Hilfethema, bestimmt dieses Abteilung und Priorität.</p>
|
||
<div class="btn-row"><button type="submit" class="btn">Speichern</button></div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>Frage ausprobieren</h3>
|
||
<p class="muted" style="font-size:.85rem;margin-bottom:10px">Zeigt, was der Assistent auf eine Kundenfrage antworten würde (ohne Zähler und ohne Ticket).</p>
|
||
<form method="post" style="display:flex;gap:8px;flex-wrap:wrap">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="test">
|
||
<input type="text" name="question" value="<?= e($testQuestion) ?>" placeholder="z. B. Wie setze ich mein Passwort zurück?" maxlength="300" style="flex:1;min-width:220px">
|
||
<button type="submit" class="btn secondary">Testen</button>
|
||
</form>
|
||
<?php if ($testResult): ?>
|
||
<div style="margin-top:12px;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface)">
|
||
<div class="muted" style="font-size:.76rem"><?= $testResult['source'] === 'custom' ? 'Zusätzliche Antwort' : 'Wissensdatenbank' ?>: <?= e($testResult['title']) ?></div>
|
||
<div style="white-space:pre-wrap;margin-top:6px;font-size:.88rem"><?= e($testResult['text']) ?></div>
|
||
<?php foreach ($testResult['others'] as $o): ?><div class="muted" style="font-size:.78rem;margin-top:4px">Weiterer Treffer: <?= e($o['title']) ?></div><?php endforeach; ?>
|
||
</div>
|
||
<?php elseif ($testResult === false): ?>
|
||
<div style="margin-top:12px" class="muted">Keine passende Antwort: Der Assistent würde anbieten, ein Ticket zu erstellen. Lege dafür unten eine Antwort an oder ergänze die Wissensdatenbank.</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="card">
|
||
<h3>Gleichbedeutende Wörter</h3>
|
||
<p class="muted" style="font-size:.85rem;margin-bottom:10px">Wörter, die Kunden und Artikel unterschiedlich nennen, aber dasselbe meinen. Eine Gruppe je Zeile, durch Komma getrennt, zum Beispiel <em>Zugang, Einladung, beitreten</em>. Der Assistent behandelt sie dann gleich. Einige Gruppen sind schon eingebaut, deine ergänzen oder überschreiben sie.</p>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="save_synonyms">
|
||
<textarea name="assistant_synonyms" rows="6" maxlength="5000" placeholder="Zugang, Einladung, beitreten Geld zurück, Erstattung, Rückzahlung"><?= e(get_setting('assistant_synonyms', '')) ?></textarea>
|
||
<div class="btn-row"><button type="submit" class="btn">Speichern</button></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($tab === 'answers'): ?>
|
||
<div class="card">
|
||
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px">
|
||
<h3 style="margin:0">Zusätzliche Antworten</h3>
|
||
<?php if (!$showForm): ?><a href="/staff/assistant.php?new=1" class="btn" style="margin-left:auto">+ Antwort hinzufügen</a><?php endif; ?>
|
||
</div>
|
||
<p class="muted" style="font-size:.85rem;margin-bottom:12px">Diese Antworten gelten zusätzlich zur Wissensdatenbank und haben Vorrang. Der Assistent findet sie über die Wörter in der Frage und in den Stichwörtern.</p>
|
||
|
||
<?php if ($showForm && !$dbOutdated): ?>
|
||
<form method="post" id="antwort-form" style="margin-bottom:18px;padding:14px;border:1px solid var(--border);border-radius:8px">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="save_answer">
|
||
<input type="hidden" name="id" value="<?= (int) ($edit['id'] ?? ($_POST['id'] ?? 0)) ?>">
|
||
<label for="title">Frage / Thema *</label>
|
||
<input type="text" id="title" name="title" maxlength="255" required value="<?= e($_POST['title'] ?? ($edit['title'] ?? $prefillTitle)) ?>" placeholder="z. B. Wie lange dauert der Versand?">
|
||
<label for="keywords" style="margin-top:10px">Stichwörter (optional, durch Komma getrennt)</label>
|
||
<input type="text" id="keywords" name="keywords" maxlength="1000" value="<?= e($_POST['keywords'] ?? ($edit['keywords'] ?? '')) ?>" placeholder="z. B. Lieferzeit, Paket, Lieferung, Sendung">
|
||
<label for="answer" style="margin-top:10px">Antwort *</label>
|
||
<textarea id="answer" name="answer" rows="5" maxlength="4000" required placeholder="Reiner Text. Adressen mit https:// werden anklickbar."><?= e($_POST['answer'] ?? ($edit['answer'] ?? '')) ?></textarea>
|
||
<label style="display:flex;align-items:center;gap:8px;margin-top:10px"><input type="checkbox" name="is_active" value="1" style="width:auto" <?= (!$edit && empty($_POST)) || !empty($edit['is_active']) || !empty($_POST['is_active']) ? 'checked' : '' ?>> Aktiv</label>
|
||
<div class="btn-row" style="gap:8px"><button type="submit" class="btn">Speichern</button><a href="/staff/assistant.php?tab=answers" class="btn secondary">Abbrechen</a></div>
|
||
</form>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!$answers): ?>
|
||
<p class="muted">Noch keine zusätzlichen Antworten.</p>
|
||
<?php else: ?>
|
||
<table>
|
||
<thead><tr><th>Frage / Thema</th><th>Stichwörter</th><th>Genutzt</th><th>Status</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($answers as $a): ?>
|
||
<tr>
|
||
<td><a href="/staff/assistant.php?edit=<?= (int) $a['id'] ?>"><?= e($a['title']) ?></a><br><span class="muted" style="font-size:.78rem"><?= e(mb_strimwidth((string) $a['answer'], 0, 90, '…')) ?></span></td>
|
||
<td class="muted"><?= e((string) ($a['keywords'] ?? '')) ?></td>
|
||
<td><?= (int) $a['hits'] ?>×</td>
|
||
<td><?= $a['is_active'] ? 'Aktiv' : '<span class="muted">Aus</span>' ?></td>
|
||
<td style="white-space:nowrap">
|
||
<a href="/staff/assistant.php?edit=<?= (int) $a['id'] ?>">Bearbeiten</a> ·
|
||
<form method="post" style="display:inline" onsubmit="return confirm('Diese Antwort löschen?')">
|
||
<?= csrf_field() ?><input type="hidden" name="action" value="delete_answer"><input type="hidden" name="id" value="<?= (int) $a['id'] ?>">
|
||
<button type="submit" class="link" style="background:none;border:0;color:var(--danger);cursor:pointer;padding:0">Löschen</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php endif; ?>
|
||
|
||
<?php if ($tab === 'log'): ?>
|
||
<div class="card" id="fragen-log">
|
||
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px">
|
||
<h3 style="margin:0">Fragen-Log</h3>
|
||
<?php if (!$logOutdated && array_sum($logStats) > 0): ?>
|
||
<form method="post" style="margin-left:auto" onsubmit="return confirm('Das gesamte Fragen-Log löschen?')">
|
||
<?= csrf_field() ?><input type="hidden" name="action" value="clear_log">
|
||
<button type="submit" class="btn secondary">Log leeren</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php if ($logOutdated): ?>
|
||
<p class="muted">Das Log ist noch nicht verfügbar. Bitte unter System-Update auf „Datenbank aktualisieren“ klicken.</p>
|
||
<?php else: ?>
|
||
<p class="muted" style="font-size:.85rem;margin-bottom:12px">Hier siehst du, was Kunden den Assistenten gefragt haben und ob er eine Antwort gefunden hat. Es werden nur Frage und Ergebnis gespeichert (kein Name, keine E-Mail-Adresse). Fragen ohne Antwort zeigen, was du noch hinterlegen solltest.</p>
|
||
|
||
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:14px">
|
||
<?php foreach ($logResults as $key => $label): ?>
|
||
<a href="<?= e($logQuery(['lr' => $logFilter['result'] === $key ? '' : $key, 'lpage' => ''])) ?>" style="text-decoration:none;color:inherit;padding:8px 12px;border:1px solid <?= $logFilter['result'] === $key ? 'var(--primary,#2563eb)' : 'var(--border)' ?>;border-radius:8px;min-width:120px">
|
||
<div style="font-size:1.2rem;font-weight:600"><?= (int) $logStats[$key] ?></div>
|
||
<div class="muted" style="font-size:.76rem"><?= e($label) ?></div>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<form method="get" action="/staff/assistant.php" style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px">
|
||
<input type="hidden" name="tab" value="log">
|
||
<select name="lr" style="width:auto">
|
||
<option value="">Alle Ergebnisse</option>
|
||
<?php foreach ($logResults as $key => $label): ?><option value="<?= e($key) ?>" <?= $logFilter['result'] === $key ? 'selected' : '' ?>><?= e($label) ?></option><?php endforeach; ?>
|
||
</select>
|
||
<select name="lp" style="width:auto">
|
||
<?php foreach (['today' => 'Heute', '7' => 'Letzte 7 Tage', '30' => 'Letzte 30 Tage', 'all' => 'Gesamter Zeitraum'] as $k => $l): ?><option value="<?= e($k) ?>" <?= $logFilter['period'] === (string) $k ? 'selected' : '' ?>><?= e($l) ?></option><?php endforeach; ?>
|
||
</select>
|
||
<input type="text" name="lq" value="<?= e($logFilter['q']) ?>" placeholder="Suchen…" maxlength="100" style="flex:1;min-width:160px">
|
||
<button type="submit" class="btn secondary">Filtern</button>
|
||
<?php if ($logFilter['result'] !== '' || $logFilter['q'] !== '' || $logFilter['period'] !== '30'): ?><a href="/staff/assistant.php?tab=log" class="btn secondary">Zurücksetzen</a><?php endif; ?>
|
||
</form>
|
||
|
||
<?php if ($logTop): ?>
|
||
<div style="margin-bottom:16px;padding:10px 12px;border:1px solid var(--border);border-radius:8px">
|
||
<strong style="font-size:.88rem">Häufige Fragen ohne Antwort</strong>
|
||
<ul style="margin:6px 0 0;padding-left:18px;font-size:.88rem">
|
||
<?php foreach ($logTop as $t): ?>
|
||
<li><?= e($t['question']) ?> <span class="muted">(<?= (int) $t['n'] ?>×)</span> · <a href="/staff/assistant.php?new=1&title=<?= e(rawurlencode($t['question'])) ?>">Antwort dazu anlegen</a></li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!$logPage['rows']): ?>
|
||
<p class="muted">Keine Fragen für diese Auswahl.</p>
|
||
<?php else: ?>
|
||
<table>
|
||
<thead><tr><th>Zeit</th><th>Frage</th><th>Ergebnis</th><th>Antwort / Ticket</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($logPage['rows'] as $r): ?>
|
||
<tr>
|
||
<td class="muted" style="white-space:nowrap"><?= e(date('d.m.Y H:i', strtotime((string) $r['created_at']))) ?></td>
|
||
<td><?= e($r['question']) ?></td>
|
||
<td><?= e($logResults[$r['result']] ?? $r['result']) ?><?= $r['helpful'] === null ? '' : ($r['helpful'] ? ' 👍' : ' 👎') ?></td>
|
||
<td class="muted"><?= $r['ticket_number'] ? e('#' . $r['ticket_number']) : e((string) ($r['matched_title'] ?? '')) ?></td>
|
||
<td style="white-space:nowrap"><?php if ($r['result'] === 'none'): ?><a href="/staff/assistant.php?new=1&title=<?= e(rawurlencode($r['question'])) ?>">Antwort anlegen</a><?php endif; ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<div class="muted" style="display:flex;gap:12px;align-items:center;margin-top:10px;font-size:.85rem">
|
||
<span><?= (int) $logPage['total'] ?> Einträge</span>
|
||
<?php if ($logPage['pages'] > 1): ?>
|
||
<span style="margin-left:auto">
|
||
<?php if ($logPage['page'] > 1): ?><a href="<?= e($logQuery(['lpage' => $logPage['page'] - 1])) ?>">← Neuere</a><?php endif; ?>
|
||
Seite <?= (int) $logPage['page'] ?> von <?= (int) $logPage['pages'] ?>
|
||
<?php if ($logPage['page'] < $logPage['pages']): ?><a href="<?= e($logQuery(['lpage' => $logPage['page'] + 1])) ?>">Ältere →</a><?php endif; ?>
|
||
</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php require __DIR__ . '/_footer.php'; ?>
|