143 lines
12 KiB
PHP
143 lines
12 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../includes/bootstrap.php';
|
|
$staff = require_admin();
|
|
$pdo = get_db();
|
|
$errors = [];
|
|
$problemId = (int) ($_GET['id'] ?? $_POST['problem_id'] ?? 0);
|
|
$activeTab = (string) ($_GET['tab'] ?? 'list');
|
|
if (!in_array($activeTab, ['list', 'create'], true)) $activeTab = 'list';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
verify_csrf();
|
|
$action = (string) ($_POST['action'] ?? '');
|
|
$activeTab = $action === 'problem_create' ? 'create' : 'list';
|
|
try {
|
|
if ($action === 'problem_create') {
|
|
$pdo->beginTransaction();
|
|
$problemId = ticket_problem_create($pdo, $_POST, (int) $staff['id']);
|
|
$ticketNumber = trim((string) ($_POST['ticket_number'] ?? ''));
|
|
if ($ticketNumber !== '') {
|
|
$ticketStmt = $pdo->prepare('SELECT id FROM tickets WHERE ticket_number = ?');
|
|
$ticketStmt->execute([$ticketNumber]);
|
|
$ticketId = (int) $ticketStmt->fetchColumn();
|
|
if (!$ticketId) throw new InvalidArgumentException('Das angegebene Ticket wurde nicht gefunden.');
|
|
ticket_problem_link($pdo, $problemId, $ticketId, (int) $staff['id']);
|
|
log_activity($pdo, $ticketId, (int) $staff['id'], 'Mit ' . ticket_problem_number($problemId) . ' verknüpft');
|
|
}
|
|
$pdo->commit();
|
|
audit_log($staff['id'], 'Problem ' . ticket_problem_number($problemId) . ' erstellt');
|
|
flash('success', ticket_problem_number($problemId) . ' wurde angelegt.');
|
|
redirect('/staff/problems.php?tab=list&id=' . $problemId);
|
|
} elseif ($action === 'problem_update') {
|
|
if (!ticket_problem_update($pdo, $problemId, $_POST, (int) $staff['id'])) throw new InvalidArgumentException('Problem nicht gefunden.');
|
|
audit_log($staff['id'], 'Problem ' . ticket_problem_number($problemId) . ' aktualisiert');
|
|
flash('success', 'Problem wurde gespeichert.');
|
|
} elseif ($action === 'ticket_link') {
|
|
$ticketNumber = trim((string) ($_POST['ticket_number'] ?? ''));
|
|
$ticketStmt = $pdo->prepare('SELECT id FROM tickets WHERE ticket_number = ?');
|
|
$ticketStmt->execute([$ticketNumber]);
|
|
$ticketId = (int) $ticketStmt->fetchColumn();
|
|
if (!$ticketId) throw new InvalidArgumentException('Das angegebene Ticket wurde nicht gefunden.');
|
|
if (!ticket_problem_link($pdo, $problemId, $ticketId, (int) $staff['id'])) throw new InvalidArgumentException('Ticket oder Problem wurde nicht gefunden.');
|
|
log_activity($pdo, $ticketId, (int) $staff['id'], 'Mit ' . ticket_problem_number($problemId) . ' verknüpft');
|
|
audit_log($staff['id'], 'Ticket mit ' . ticket_problem_number($problemId) . ' verknüpft');
|
|
flash('success', 'Ticket wurde mit dem Problem verknüpft.');
|
|
} elseif ($action === 'ticket_unlink') {
|
|
$ticketId = (int) ($_POST['ticket_id'] ?? 0);
|
|
ticket_problem_unlink($pdo, $problemId, $ticketId);
|
|
log_activity($pdo, $ticketId, (int) $staff['id'], 'Verknüpfung mit ' . ticket_problem_number($problemId) . ' entfernt');
|
|
audit_log($staff['id'], 'Ticket von ' . ticket_problem_number($problemId) . ' getrennt');
|
|
flash('success', 'Ticket-Verknüpfung wurde entfernt.');
|
|
}
|
|
} catch (Throwable $e) {
|
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
|
$errors[] = $e instanceof InvalidArgumentException ? $e->getMessage() : 'Änderung konnte nicht gespeichert werden.';
|
|
}
|
|
if (!$errors) redirect('/staff/problems.php' . ($problemId ? '?id=' . $problemId : ''));
|
|
}
|
|
|
|
$problems = ticket_problems($pdo);
|
|
$problem = $problemId ? ticket_problem_get($pdo, $problemId) : null;
|
|
if ($problemId && !$problem) $errors[] = 'Problem nicht gefunden.';
|
|
$linkedTickets = $problem ? ticket_problem_tickets($pdo, $problemId) : [];
|
|
$problemStatuses = ticket_problem_statuses();
|
|
$activePage = 'problems';
|
|
$pageTitle = 'Problemmanagement';
|
|
require __DIR__ . '/_header.php';
|
|
?>
|
|
<h1>Problemmanagement</h1>
|
|
<p class="muted">Bündeln Sie Tickets mit derselben Ursache. Ursache, Workaround und dauerhafte Lösung werden zentral dokumentiert.</p>
|
|
<?php foreach ($errors as $error): ?><div class="flash error"><?= e($error) ?></div><?php endforeach; ?>
|
|
<nav class="problem-tabs" role="tablist" aria-label="Problemmanagement">
|
|
<a role="tab" aria-selected="<?= $activeTab === 'list' ? 'true' : 'false' ?>" class="<?= $activeTab === 'list' ? 'active' : '' ?>" href="/staff/problems.php?tab=list<?= $problemId ? '&id=' . $problemId : '' ?>">Probleme</a>
|
|
<a role="tab" aria-selected="<?= $activeTab === 'create' ? 'true' : 'false' ?>" class="<?= $activeTab === 'create' ? 'active' : '' ?>" href="/staff/problems.php?tab=create">Neues Problem</a>
|
|
</nav>
|
|
<?php if ($activeTab === 'create'): ?>
|
|
<section class="card">
|
|
<h2 style="margin-top:0">Neues Problem erfassen</h2>
|
|
<p class="muted">Titel und erste Ticketnummer eintragen. Ursache und dauerhafte Lösung können danach ergänzt werden.</p>
|
|
<form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="problem_create">
|
|
<label for="new_problem_title">Titel</label><input id="new_problem_title" type="text" name="title" maxlength="200" required placeholder="Kurze Problembeschreibung">
|
|
<label for="new_problem_ticket" style="margin-top:8px">Ticket verknüpfen (optional)</label><input id="new_problem_ticket" type="text" name="ticket_number" placeholder="Ticketnummer">
|
|
<label for="new_problem_description" style="margin-top:8px">Beschreibung / Auswirkungen</label><textarea id="new_problem_description" name="description" rows="3"></textarea>
|
|
<label for="new_problem_cause" style="margin-top:8px">Ursache (falls bekannt)</label><textarea id="new_problem_cause" name="root_cause" rows="3"></textarea>
|
|
<label for="new_problem_workaround" style="margin-top:8px">Workaround (optional)</label><textarea id="new_problem_workaround" name="workaround" rows="3"></textarea>
|
|
<button class="btn" type="submit" style="margin-top:12px">Problem anlegen</button>
|
|
</form>
|
|
</section>
|
|
<?php else: ?>
|
|
<div style="display:grid;grid-template-columns:minmax(240px,320px) minmax(0,1fr);gap:16px;align-items:start">
|
|
<aside class="card">
|
|
<h3 style="margin-top:0">Probleme</h3>
|
|
<?php if (!$problems): ?><p class="muted">Noch keine Probleme erfasst.</p><?php endif; ?>
|
|
<?php foreach ($problems as $item): ?>
|
|
<a href="/staff/problems.php?id=<?= (int) $item['id'] ?>" style="display:block;padding:9px 4px;border-top:1px solid var(--border,#e3e9ee);text-decoration:none;<?= (int) $item['id'] === $problemId ? 'font-weight:700;color:var(--primary)' : '' ?>">
|
|
<strong><?= e(ticket_problem_number((int) $item['id'])) ?></strong> · <?= e($item['title']) ?><br>
|
|
<span class="muted" style="font-size:.78rem"><?= e($problemStatuses[$item['status']] ?? $item['status']) ?> · <?= (int) $item['ticket_count'] ?> Tickets</span>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</aside>
|
|
|
|
<main>
|
|
<?php if (!$problem): ?>
|
|
<section class="card"><h3>Problem auswählen</h3><p class="muted">Wählen Sie links ein Problem aus oder legen Sie ein neues an.</p></section>
|
|
<?php else: ?>
|
|
<section class="card">
|
|
<div style="display:flex;justify-content:space-between;align-items:start;gap:12px;flex-wrap:wrap">
|
|
<div><span class="muted"><?= e(ticket_problem_number($problemId)) ?></span><h2 style="margin:2px 0 12px"><?= e($problem['title']) ?></h2></div>
|
|
<span class="muted">Erstellt <?= e(format_dt((string) $problem['created_at'])) ?><?= $problem['creator_name'] ? ' von ' . e($problem['creator_name']) : '' ?></span>
|
|
</div>
|
|
<form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="problem_update"><input type="hidden" name="problem_id" value="<?= $problemId ?>">
|
|
<label for="problem_title">Titel</label><input id="problem_title" type="text" name="title" maxlength="200" required value="<?= e($problem['title']) ?>">
|
|
<label for="problem_description" style="margin-top:8px">Beschreibung / Auswirkungen</label><textarea id="problem_description" name="description" rows="3"><?= e((string) ($problem['description'] ?? '')) ?></textarea>
|
|
<label for="problem_root_cause" style="margin-top:8px">Ursache</label><textarea id="problem_root_cause" name="root_cause" rows="3"><?= e((string) ($problem['root_cause'] ?? '')) ?></textarea>
|
|
<label for="problem_workaround" style="margin-top:8px">Workaround</label><textarea id="problem_workaround" name="workaround" rows="3"><?= e((string) ($problem['workaround'] ?? '')) ?></textarea>
|
|
<label for="problem_resolution" style="margin-top:8px">Dauerhafte Lösung</label><textarea id="problem_resolution" name="resolution" rows="3"><?= e((string) ($problem['resolution'] ?? '')) ?></textarea>
|
|
<div style="display:flex;gap:12px;align-items:end;flex-wrap:wrap;margin-top:8px"><div><label for="problem_status">Status</label><select id="problem_status" name="status"><?php foreach ($problemStatuses as $key => $label): ?><option value="<?= e($key) ?>" <?= $problem['status'] === $key ? 'selected' : '' ?>><?= e($label) ?></option><?php endforeach; ?></select></div><button class="btn" type="submit">Problem speichern</button></div>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="card" style="margin-top:16px">
|
|
<h3 style="margin-top:0">Verknüpfte Tickets (<?= count($linkedTickets) ?>)</h3>
|
|
<form method="post" style="display:flex;gap:8px;flex-wrap:wrap;align-items:end;margin-bottom:12px"><?= csrf_field() ?><input type="hidden" name="action" value="ticket_link"><input type="hidden" name="problem_id" value="<?= $problemId ?>"><div style="flex:1;min-width:200px"><label for="link_ticket_number">Ticketnummer</label><input id="link_ticket_number" type="text" name="ticket_number" required placeholder="z. B. 260001"></div><button class="btn secondary" type="submit">Ticket verknüpfen</button></form>
|
|
<?php if (!$linkedTickets): ?><p class="muted">Noch keine Tickets mit diesem Problem verknüpft.</p><?php endif; ?>
|
|
<?php foreach ($linkedTickets as $linked): ?>
|
|
<div style="display:flex;justify-content:space-between;gap:10px;align-items:center;border-top:1px solid var(--border,#e3e9ee);padding:9px 0">
|
|
<div><a href="/staff/ticket_view.php?n=<?= urlencode((string) $linked['ticket_number']) ?>"><strong>#<?= e($linked['ticket_number']) ?></strong> · <?= e($linked['subject']) ?></a><div class="muted" style="font-size:.78rem"><?= e($linked['requester_name']) ?> · <?= e(status_label((string) $linked['status'])[0]) ?></div></div>
|
|
<form method="post" onsubmit="return confirm('Ticket-Verknüpfung entfernen?')"><?= csrf_field() ?><input type="hidden" name="action" value="ticket_unlink"><input type="hidden" name="problem_id" value="<?= $problemId ?>"><input type="hidden" name="ticket_id" value="<?= (int) $linked['id'] ?>"><button class="btn secondary" type="submit">Trennen</button></form>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</section>
|
|
<?php endif; ?>
|
|
</main>
|
|
</div>
|
|
<?php endif; ?>
|
|
<style>
|
|
.problem-tabs{display:flex;gap:8px;margin:18px 0 12px;border-bottom:1px solid var(--border,#d8e0e8)}
|
|
.problem-tabs a{border-bottom:3px solid transparent;border-radius:7px 7px 0 0;background:transparent;color:var(--muted,#64748b);padding:10px 16px;font-weight:700;text-decoration:none}
|
|
.problem-tabs a.active{color:var(--text,#0f172a);border-bottom-color:var(--primary,#0f766e);background:var(--surface,#fff)}
|
|
|
|
@media(max-width:760px){.card+main{min-width:0}}
|
|
</style>
|
|
<?php require __DIR__ . '/_footer.php'; ?>
|