Files
2026-10-16 19:12:24 +00:00

203 lines
9.4 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../includes/bootstrap.php';
$staff = require_staff_login();
$pdo = get_db();
$errors = [];
$allStaff = $pdo->query('SELECT id, name FROM staff WHERE is_active = 1 ORDER BY name')->fetchAll();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
$action = $_POST['action'] ?? '';
if ($action === 'create') {
$title = trim($_POST['title'] ?? '');
$desc = trim($_POST['description'] ?? '');
$assignTo = ($_POST['assigned_to'] ?? '') !== '' ? (int)$_POST['assigned_to'] : null;
$dueAt = trim($_POST['due_at'] ?? '') ?: null;
$ticketId = ($_POST['ticket_id'] ?? '') !== '' ? (int)$_POST['ticket_id'] : null;
if ($title === '') $errors[] = 'Bitte einen Titel angeben.';
if (!$errors) {
$pdo->prepare('INSERT INTO tasks (ticket_id,title,description,assigned_to,due_at,created_by,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)')
->execute([$ticketId, $title, $desc ?: null, $assignTo, $dueAt, $staff['id'], db_now(), db_now()]);
flash('success', 'Aufgabe erstellt.');
if ($ticketId) {
$tn = $pdo->prepare('SELECT ticket_number FROM tickets WHERE id=?'); $tn->execute([$ticketId]);
redirect('/staff/ticket_view.php?n=' . urlencode($tn->fetchColumn()));
}
redirect('/staff/tasks.php');
}
}
if ($action === 'update_status') {
$id = (int) $_POST['id'];
$status = in_array($_POST['status']??'', ['open','in_progress','done']) ? $_POST['status'] : 'open';
$pdo->prepare('UPDATE tasks SET status=?, updated_at=? WHERE id=?')->execute([$status, db_now(), $id]);
// Kein open redirect: Referer nur wenn er auf /staff/ zeigt
$ref = $_SERVER['HTTP_REFERER'] ?? '';
$safePath = (parse_url($ref, PHP_URL_HOST) === ($_SERVER['HTTP_HOST'] ?? '') && strpos(parse_url($ref, PHP_URL_PATH) ?? '', '/staff/') === 0)
? $ref : '/staff/tasks.php';
redirect($safePath);
}
if ($action === 'delete') {
$pdo->prepare('DELETE FROM tasks WHERE id=?')->execute([(int)$_POST['id']]);
flash('success', 'Aufgabe gelöscht.');
redirect('/staff/tasks.php');
}
}
$filter = $_GET['filter'] ?? 'my'; // my, all, open, done
$where = [];
$params = [];
if ($filter === 'my') {
$where[] = 'ta.assigned_to = ?'; $params[] = $staff['id'];
} elseif ($filter === 'open') {
$where[] = "ta.status != 'done'";
} elseif ($filter === 'done') {
$where[] = "ta.status = 'done'";
}
$sql = 'SELECT ta.*, s.name AS assigned_name, s2.name AS creator_name,
t.ticket_number, t.subject AS ticket_subject
FROM tasks ta
LEFT JOIN staff s ON s.id = ta.assigned_to
LEFT JOIN staff s2 ON s2.id = ta.created_by
LEFT JOIN tickets t ON t.id = ta.ticket_id'
. ($where ? ' WHERE ' . implode(' AND ', $where) : '')
. ' ORDER BY ta.due_at IS NULL, ta.due_at ASC, ta.created_at DESC LIMIT 200';
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$tasks = $stmt->fetchAll();
$statusLabels = ['open'=>'Offen','in_progress'=>'In Arbeit','done'=>'Erledigt'];
$statusColors = ['open'=>'#2563eb','in_progress'=>'#d97706','done'=>'#16a34a'];
$pageTitle = 'Aufgaben'; $activePage = 'tasks';
require __DIR__ . '/_header.php';
?>
<div class="topbar">
<h1>Aufgaben</h1>
<button type="button" class="btn" onclick="document.getElementById('new-task-form').style.display=document.getElementById('new-task-form').style.display==='none'?'':'none'">+ Neue Aufgabe</button>
</div>
<?php foreach ($errors as $e): ?><div class="flash error"><?= e($e) ?></div><?php endforeach; ?>
<!-- Neue Aufgabe -->
<div id="new-task-form" class="card" style="display:none; margin-bottom:20px">
<h3>Neue Aufgabe</h3>
<form method="post">
<?= csrf_field() ?>
<input type="hidden" name="action" value="create">
<div class="field-row">
<div style="flex:2">
<label for="title">Titel <span style="color:var(--danger)">*</span></label>
<input type="text" id="title" name="title" placeholder="Was ist zu tun?" required>
</div>
<div>
<label for="due_at">Fällig bis</label>
<input type="datetime-local" id="due_at" name="due_at">
</div>
</div>
<div class="field-row">
<div>
<label for="assigned_to">Zuweisen an</label>
<select name="assigned_to" id="assigned_to">
<option value="<?= $staff['id'] ?>" selected><?= e($staff['name']) ?> (ich)</option>
<?php foreach ($allStaff as $s): if ($s['id'] == $staff['id']) continue; ?>
<option value="<?= $s['id'] ?>"><?= e($s['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="ticket_id_text">Ticket verknüpfen (Ticket-Nr., optional)</label>
<input type="text" id="ticket_id_text" placeholder="z.B. 260622-1234" oninput="lookupTicket(this.value)">
<input type="hidden" name="ticket_id" id="ticket_id_hidden">
<div id="ticket_lookup_result" style="font-size:.78rem; color:var(--muted); margin-top:2px"></div>
</div>
</div>
<label for="description">Beschreibung (optional)</label>
<textarea id="description" name="description" style="min-height:80px"></textarea>
<div class="btn-row">
<button type="submit" class="btn">Erstellen</button>
<button type="button" class="btn secondary" onclick="document.getElementById('new-task-form').style.display='none'">Abbrechen</button>
</div>
</form>
</div>
<!-- Filter-Tabs -->
<div style="display:flex; gap:0; border-bottom:2px solid var(--border); margin-bottom:16px">
<?php foreach (['my'=>'Meine','all'=>'Alle','open'=>'Offen','done'=>'Erledigt'] as $f=>$lbl): ?>
<a href="/staff/tasks.php?filter=<?= $f ?>"
style="padding:8px 18px; font-size:.88rem; font-weight:600; text-decoration:none; border-bottom:3px solid <?= $filter===$f?'var(--primary)':'transparent' ?>; margin-bottom:-2px; color:<?= $filter===$f?'var(--primary)':'var(--muted)' ?>">
<?= $lbl ?>
</a>
<?php endforeach; ?>
</div>
<div class="card">
<table>
<thead><tr><th>Aufgabe</th><th>Verknüpftes Ticket</th><th>Zugewiesen</th><th>Fällig</th><th>Status</th><th></th></tr></thead>
<tbody>
<?php foreach ($tasks as $task):
$overdue = $task['due_at'] && $task['status'] !== 'done' && strtotime($task['due_at']) < time();
?>
<tr class="<?= $overdue ? 'is-overdue' : '' ?>">
<td>
<strong style="<?= $task['status']==='done' ? 'text-decoration:line-through;color:var(--muted)' : '' ?>"><?= e($task['title']) ?></strong>
<?php if ($task['description']): ?><br><span class="muted" style="font-size:.78rem"><?= e(mb_strimwidth($task['description'], 0, 80, '…')) ?></span><?php endif; ?>
</td>
<td>
<?php if ($task['ticket_number']): ?>
<a href="/staff/ticket_view.php?n=<?= e($task['ticket_number']) ?>" class="ticket-no">#<?= e($task['ticket_number']) ?></a><br>
<span class="muted" style="font-size:.76rem"><?= e(mb_strimwidth($task['ticket_subject']??'', 0, 40, '…')) ?></span>
<?php else: ?><span class="muted">–</span><?php endif; ?>
</td>
<td><?= e($task['assigned_name'] ?? '–') ?></td>
<td class="muted" style="white-space:nowrap; <?= $overdue ? 'color:var(--danger)' : '' ?>">
<?= $task['due_at'] ? format_dt($task['due_at']) : '–' ?>
</td>
<td>
<form method="post" style="display:inline">
<?= csrf_field() ?>
<input type="hidden" name="action" value="update_status">
<input type="hidden" name="id" value="<?= $task['id'] ?>">
<select name="status" onchange="this.form.submit()" style="padding:3px 6px; font-size:.78rem; border-radius:5px">
<?php foreach ($statusLabels as $sv=>$sl): ?>
<option value="<?= $sv ?>" <?= $task['status']===$sv?'selected':'' ?>><?= $sl ?></option>
<?php endforeach; ?>
</select>
</form>
</td>
<td>
<form method="post" style="display:inline" onsubmit="return confirm('Aufgabe löschen?')">
<?= csrf_field() ?>
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $task['id'] ?>">
<button type="submit" style="background:none;border:none;color:#dc2626;cursor:pointer;padding:0;font-size:.85rem">✕</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php if (!$tasks): ?><tr><td colspan="6" class="muted" style="text-align:center;padding:28px">Keine Aufgaben gefunden.</td></tr><?php endif; ?>
</tbody>
</table>
</div>
<script>
function lookupTicket(num) {
var n = num.trim();
var hidden = document.getElementById('ticket_id_hidden');
var result = document.getElementById('ticket_lookup_result');
if (!n) { hidden.value=''; result.textContent=''; return; }
fetch('/staff/_ticket_lookup_ajax.php?n=' + encodeURIComponent(n))
.then(r=>r.json()).then(d=>{
if (d.id) { hidden.value=d.id; result.textContent='✓ ' + d.subject; result.style.color='var(--success)'; }
else { hidden.value=''; result.textContent='Ticket nicht gefunden'; result.style.color='var(--danger)'; }
}).catch(()=>{hidden.value='';result.textContent='';});
}
</script>
<?php require __DIR__ . '/_footer.php'; ?>