265 lines
14 KiB
PHP
265 lines
14 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||
$admin = require_admin_or_permission('manage_help_topics');
|
||
$pdo = get_db();
|
||
|
||
$errors = [];
|
||
$departments = departments_for_select($pdo, array_map('intval', $pdo->query('SELECT department_id FROM help_topics')->fetchAll(PDO::FETCH_COLUMN)));
|
||
$priorities = $pdo->query('SELECT * FROM priorities ORDER BY sort_order')->fetchAll();
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
$action = $_POST['action'] ?? '';
|
||
|
||
if ($action === 'create' || $action === 'update') {
|
||
$id = $action === 'update' ? (int) ($_POST['id'] ?? 0) : 0;
|
||
try {
|
||
help_topic_save($pdo, $id, [
|
||
'name' => $_POST['name'] ?? '', 'department_id' => $_POST['department_id'] ?? 0, 'priority_id' => $_POST['priority_id'] ?? 0,
|
||
'is_active' => ($_POST['status'] ?? 'active') === 'active', 'is_public' => ($_POST['type'] ?? 'public') === 'public',
|
||
'parent_id' => $_POST['parent_id'] ?? null, 'sla_plan_id' => $_POST['sla_plan_id'] ?? null,
|
||
'notes' => $_POST['notes'] ?? '', 'sort_order' => $_POST['sort_order'] ?? 0,
|
||
]);
|
||
audit_log($admin['id'], ($action === 'create' ? 'Hilfethema erstellt: ' : 'Hilfethema aktualisiert: ') . trim($_POST['name'] ?? ''));
|
||
flash('success', $action === 'create' ? 'Hilfethema erstellt.' : 'Änderungen gespeichert.');
|
||
redirect('/staff/help_topics.php');
|
||
} catch (AdminModelException $e) {
|
||
$errors[] = $e->getMessage();
|
||
}
|
||
}
|
||
|
||
if ($action === 'delete') {
|
||
$deleted = help_topic_delete($pdo, (int) $_POST['id']);
|
||
audit_log($admin['id'], "Hilfethema gelöscht: {$deleted}");
|
||
flash('success', 'Hilfethema gelöscht.');
|
||
redirect('/staff/help_topics.php');
|
||
}
|
||
}
|
||
|
||
$isNew = isset($_GET['new']);
|
||
$editId = (int) ($_GET['edit'] ?? 0);
|
||
$showForm = $isNew || $editId || ($errors && in_array($_POST['action'] ?? '', ['create', 'update'], true));
|
||
|
||
$editTopic = null;
|
||
if ($editId) {
|
||
$stmt = $pdo->prepare('SELECT * FROM help_topics WHERE id = ?');
|
||
$stmt->execute([$editId]);
|
||
$editTopic = $stmt->fetch() ?: null;
|
||
}
|
||
|
||
$topicFields = $globalFields = [];
|
||
$fieldTypeLabels = ['text' => 'Einzeiliger Text', 'textarea' => 'Mehrzeiliger Text', 'select' => 'Dropdown-Auswahl', 'checkbox' => 'Checkbox (Ja/Nein)', 'number' => 'Zahl', 'date' => 'Datum'];
|
||
if ($editTopic) {
|
||
$cfStmt = $pdo->prepare('SELECT * FROM custom_fields WHERE help_topic_id = ? ORDER BY sort_order, label');
|
||
$cfStmt->execute([$editTopic['id']]);
|
||
$topicFields = $cfStmt->fetchAll();
|
||
$globalFields = $pdo->query('SELECT * FROM custom_fields WHERE help_topic_id IS NULL AND is_active = 1 ORDER BY sort_order, label')->fetchAll();
|
||
}
|
||
$sortMode = ($_GET['sort'] ?? 'alpha') === 'manual' ? 'manual' : 'alpha';
|
||
$topics = $pdo->query(
|
||
'SELECT h.*, d.name AS department_name, p.name AS priority_name
|
||
FROM help_topics h
|
||
JOIN departments d ON d.id = h.department_id
|
||
JOIN priorities p ON p.id = h.priority_id
|
||
ORDER BY ' . ($sortMode === 'manual' ? 'h.sort_order, h.name' : 'h.name')
|
||
)->fetchAll();
|
||
// Anzeigename mit Elternpfad
|
||
$names = array_column($topics, 'name', 'id');
|
||
$parents = array_column($topics, 'parent_id', 'id');
|
||
$pathOf = function (int $id) use ($names, $parents): string {
|
||
$parts = []; $seen = [];
|
||
while ($id && isset($names[$id]) && !isset($seen[$id])) { $seen[$id] = true; array_unshift($parts, $names[$id]); $id = (int) ($parents[$id] ?? 0); }
|
||
return implode(' / ', $parts);
|
||
};
|
||
$fmt = fn(?string $dt) => $dt ? date('d.m.Y H:i', strtotime($dt)) : '–';
|
||
|
||
$pageTitle = 'Hilfethemen';
|
||
$activePage = 'help_topics';
|
||
require __DIR__ . '/_header.php';
|
||
?>
|
||
|
||
<?php foreach ($errors as $err): ?>
|
||
<div class="flash error"><?= e($err) ?></div>
|
||
<?php endforeach; ?>
|
||
|
||
<?php if ($showForm): ?>
|
||
<?php
|
||
$f = fn(string $k, $default = '') => $_POST[$k] ?? $default;
|
||
$v = fn(string $k, string $col, $default = '') => $f($k, $editTopic[$col] ?? $default);
|
||
$curStatus = $_POST ? ($_POST['status'] ?? 'active') : (($editTopic['is_active'] ?? 1) ? 'active' : 'inactive');
|
||
$curType = $_POST ? ($_POST['type'] ?? 'public') : (($editTopic['is_public'] ?? 1) ? 'public' : 'internal');
|
||
$curParent = (string) $v('parent_id', 'parent_id');
|
||
$excluded = $editTopic ? array_merge([(int) $editTopic['id']], help_topic_descendants($pdo, (int) $editTopic['id'])) : [];
|
||
?>
|
||
<h1>
|
||
<?= $editTopic ? 'Hilfethema aktualisieren' : 'Neues Hilfethema' ?>
|
||
<?php if ($editTopic): ?><span class="muted" style="font-size:1rem;font-weight:400">— <?= e($editTopic['name']) ?></span><?php endif; ?>
|
||
</h1>
|
||
|
||
<form method="post" class="card" style="padding:0">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="<?= $editTopic ? 'update' : 'create' ?>">
|
||
<?php if ($editTopic): ?><input type="hidden" name="id" value="<?= (int) $editTopic['id'] ?>"><?php endif; ?>
|
||
|
||
<div style="display:flex;gap:4px;padding:10px 12px 0;border-bottom:1px solid var(--border)">
|
||
<button type="button" class="tab-btn" data-tab="info">Hilfethemeninformationen</button>
|
||
<button type="button" class="tab-btn" data-tab="options">Optionen bei neuem Ticket</button>
|
||
<button type="button" class="tab-btn" data-tab="forms">Formulare</button>
|
||
</div>
|
||
|
||
<div class="tab-pane" data-pane="info" style="padding:16px 20px">
|
||
<label for="name">Thema *</label>
|
||
<input type="text" id="name" name="name" value="<?= e($v('name', 'name')) ?>" placeholder="z. B. Technisches Problem" required>
|
||
<label for="status">Status *</label>
|
||
<select id="status" name="status">
|
||
<option value="active" <?= $curStatus === 'active' ? 'selected' : '' ?>>Aktiv</option>
|
||
<option value="inactive" <?= $curStatus === 'inactive' ? 'selected' : '' ?>>Deaktiviert</option>
|
||
</select>
|
||
<label>Typ *</label>
|
||
<div style="display:flex;gap:18px;margin:4px 0 10px">
|
||
<label style="display:flex;align-items:center;gap:6px;margin:0"><input type="radio" name="type" value="public" style="width:auto" <?= $curType === 'public' ? 'checked' : '' ?>> Öffentlich</label>
|
||
<label style="display:flex;align-items:center;gap:6px;margin:0"><input type="radio" name="type" value="internal" style="width:auto" <?= $curType === 'internal' ? 'checked' : '' ?>> Vertraulich/Intern</label>
|
||
</div>
|
||
<p class="muted" style="font-size:.78rem;margin-top:-6px">Öffentliche Themen sehen auch Kunden im Portal; interne nur Mitarbeiter.</p>
|
||
<label for="parent_id">Übergeordnetes Thema</label>
|
||
<select id="parent_id" name="parent_id">
|
||
<option value="">— Oberste Hilfethemenebene —</option>
|
||
<?php foreach ($topics as $t): if (in_array((int) $t['id'], $excluded, true)) continue; ?>
|
||
<option value="<?= $t['id'] ?>" <?= $curParent === (string) $t['id'] ? 'selected' : '' ?>><?= e($pathOf((int) $t['id'])) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label for="notes">Interne Notizen <span class="muted" style="font-weight:400">– Sei offen, sie sind vertraulich</span></label>
|
||
<textarea id="notes" name="notes" rows="8"><?= e($v('notes', 'notes')) ?></textarea>
|
||
</div>
|
||
|
||
<div class="tab-pane" data-pane="options" style="padding:16px 20px;display:none">
|
||
<p class="muted" style="font-size:.82rem;margin-top:0">Diese Werte werden beim Anlegen eines Tickets mit diesem Thema automatisch gesetzt.</p>
|
||
<label for="department_id">Abteilung *</label>
|
||
<select name="department_id" id="department_id" required>
|
||
<?php foreach ($departments as $d): ?>
|
||
<option value="<?= $d['id'] ?>" <?= (string) $v('department_id', 'department_id') === (string) $d['id'] ? 'selected' : '' ?>><?= e($d['name']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label for="priority_id">Priorität *</label>
|
||
<select name="priority_id" id="priority_id" required>
|
||
<?php foreach ($priorities as $p): ?>
|
||
<option value="<?= $p['id'] ?>" <?= (string) $v('priority_id', 'priority_id') === (string) $p['id'] ? 'selected' : '' ?>><?= e($p['name']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label for="sla_plan_id">SLA-Plan</label>
|
||
<select name="sla_plan_id" id="sla_plan_id">
|
||
<option value="">— Abteilung/Priorität bestimmt die Zeiten —</option>
|
||
<?php foreach ($pdo->query('SELECT id, name, is_active FROM sla_plans ORDER BY name')->fetchAll() as $__sp): ?>
|
||
<option value="<?= $__sp['id'] ?>" <?= (string) $v('sla_plan_id', 'sla_plan_id') === (string) $__sp['id'] ? 'selected' : '' ?>><?= e($__sp['name']) ?><?= $__sp['is_active'] ? '' : ' (deaktiviert)' ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label for="sort_order">Sortierung</label>
|
||
<input type="number" id="sort_order" name="sort_order" value="<?= e((string) $v('sort_order', 'sort_order', '0')) ?>">
|
||
<p class="muted" style="font-size:.78rem;margin-top:10px">Zusatzfelder pro Thema legst du unter <a href="/staff/custom_fields.php">Zusatzfelder</a> an.</p>
|
||
</div>
|
||
|
||
<div class="tab-pane" data-pane="forms" style="padding:16px 20px;display:none">
|
||
<h3 style="margin-top:0">Formulare</h3>
|
||
<?php if (!$editTopic): ?>
|
||
<p class="muted">Zusatzfelder lassen sich nach dem Erstellen des Hilfethemas zuordnen.</p>
|
||
<?php else: ?>
|
||
<p class="muted" style="margin-top:0">Diese Zusatzfelder erscheinen auf dem Ticketformular, sobald Kunden dieses Hilfethema wählen.</p>
|
||
<?php if (!$topicFields): ?>
|
||
<p class="muted">Für dieses Thema gibt es noch keine eigenen Felder.</p>
|
||
<?php else: ?>
|
||
<table>
|
||
<thead><tr><th>Bezeichnung</th><th>Typ</th><th>Pflicht</th><th>Aktiv</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($topicFields as $cf): ?>
|
||
<tr>
|
||
<td><?= e($cf['label']) ?></td>
|
||
<td><?= e($fieldTypeLabels[$cf['field_type']] ?? $cf['field_type']) ?></td>
|
||
<td><?= $cf['is_required'] ? '✓' : '–' ?></td>
|
||
<td><?= $cf['is_active'] ? '✓' : '–' ?></td>
|
||
<td><a href="/staff/custom_fields.php?edit=<?= (int) $cf['id'] ?>">Bearbeiten</a></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
<p style="margin-top:12px"><a class="btn secondary" href="/staff/custom_fields.php?topic=<?= (int) $editTopic['id'] ?>">+ Neues Feld für dieses Thema</a></p>
|
||
<?php if ($globalFields): ?>
|
||
<h4 style="border-bottom:1px dotted var(--border);padding-bottom:4px">Felder für alle Themen</h4>
|
||
<p class="muted" style="font-size:.82rem;margin-top:0">Gelten zusätzlich bei jedem Thema und werden unter <a href="/staff/custom_fields.php">Ticket-Felder</a> verwaltet.</p>
|
||
<ul style="margin:0 0 0 18px"><?php foreach ($globalFields as $cf): ?><li><?= e($cf['label']) ?><?= $cf['is_required'] ? ' *' : '' ?></li><?php endforeach; ?></ul>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="btn-row" style="padding:0 20px 18px">
|
||
<button type="submit" class="btn">Änderungen speichern</button>
|
||
<a href="/staff/help_topics.php" class="btn secondary">Abbrechen</a>
|
||
</div>
|
||
</form>
|
||
|
||
<style>
|
||
.tab-btn{background:none;border:1px solid transparent;border-bottom:none;padding:8px 16px;cursor:pointer;border-radius:6px 6px 0 0;color:var(--muted)}
|
||
.tab-btn.active{background:var(--card-bg,#fff);border-color:var(--border);color:inherit;font-weight:600;position:relative;top:1px}
|
||
</style>
|
||
<script>
|
||
(function () {
|
||
var btns = document.querySelectorAll('.tab-btn'), panes = document.querySelectorAll('.tab-pane');
|
||
function show(t) {
|
||
btns.forEach(function (b) { b.classList.toggle('active', b.dataset.tab === t); });
|
||
panes.forEach(function (p) { p.style.display = p.dataset.pane === t ? '' : 'none'; });
|
||
}
|
||
btns.forEach(function (b) { b.addEventListener('click', function () { show(b.dataset.tab); }); });
|
||
show('info');
|
||
})();
|
||
</script>
|
||
|
||
<?php else: ?>
|
||
<div style="display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap">
|
||
<h1 style="margin:0">Hilfethemen</h1>
|
||
<div style="display:flex;gap:12px;align-items:center">
|
||
<form method="get" style="display:flex;align-items:center;gap:6px;margin:0">
|
||
<label for="sort" style="margin:0">Sortiermodus:</label>
|
||
<select id="sort" name="sort" onchange="this.form.submit()" style="width:auto">
|
||
<option value="alpha" <?= $sortMode === 'alpha' ? 'selected' : '' ?>>Alphabetisch</option>
|
||
<option value="manual" <?= $sortMode === 'manual' ? 'selected' : '' ?>>Manuell</option>
|
||
</select>
|
||
</form>
|
||
<a href="/staff/help_topics.php?new=1" class="btn">Neues Hilfethema hinzufügen</a>
|
||
</div>
|
||
</div>
|
||
<p class="muted">Hilfethemen erscheinen im Kundenportal als Auswahl bei der Ticket-Erstellung und setzen automatisch Abteilung und Priorität.</p>
|
||
|
||
<div class="card">
|
||
<table>
|
||
<thead><tr><th>Hilfethema</th><th>Status</th><th>Typ</th><th>Priorität</th><th>Abteilung</th><th>Zuletzt aktualisiert</th><th>Erstellt</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($topics as $t): ?>
|
||
<tr>
|
||
<td><a href="/staff/help_topics.php?edit=<?= (int) $t['id'] ?>"><?= e($pathOf((int) $t['id'])) ?></a></td>
|
||
<td><strong><?= $t['is_active'] ? 'Aktiv' : 'Deaktiviert' ?></strong></td>
|
||
<td><?= ($t['is_public'] ?? 1) ? 'Öffentlich' : 'Intern' ?></td>
|
||
<td><?= e($t['priority_name']) ?></td>
|
||
<td><?= e($t['department_name']) ?></td>
|
||
<td class="muted" style="font-size:.82rem"><?= e($fmt($t['updated_at'] ?? null)) ?></td>
|
||
<td class="muted" style="font-size:.82rem"><?= e($fmt($t['created_at'] ?? null)) ?></td>
|
||
<td style="white-space:nowrap">
|
||
<a href="/staff/help_topics.php?edit=<?= (int) $t['id'] ?>">Bearbeiten</a> ·
|
||
<form method="post" style="display:inline" onsubmit="return confirm('Hilfethema wirklich löschen?');">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="delete">
|
||
<input type="hidden" name="id" value="<?= (int) $t['id'] ?>">
|
||
<button type="submit" style="background:none;border:none;color:#dc2626;cursor:pointer;padding:0;font-size:.9rem">Löschen</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (!$topics): ?>
|
||
<tr><td colspan="8" class="muted">Noch keine Hilfethemen angelegt.</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php require __DIR__ . '/_footer.php'; ?>
|