Files
deskhub/DeskHub-Webseite/staff/live_tickets.php
T
2026-10-26 21:19:10 +00:00

65 lines
3.4 KiB
PHP

<?php
/**
* staff/live_tickets.php
*
* Leichter Abruf für die Live-Anzeige im Mitarbeiterbereich (assets/js/live-tickets.js): aktuelle Kennzahlen und Tickets, die seit der
* zuletzt gesehenen Ticket-ID neu angelegt wurden. Jeder sieht nur Tickets seiner Abteilungen.
* ?since=0 liefert nur den Ausgangsstand (keine „neuen“ Tickets), damit beim ersten Aufruf nichts aufpoppt.
*/
require_once __DIR__ . '/../includes/bootstrap.php';
$staff = current_staff();
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
if (!$staff) {
http_response_code(401);
echo json_encode(['error' => 'login']);
exit;
}
// Abgelaufene Sitzung (Inaktivität) gilt auch hier als abgemeldet; dieser Abruf verlängert die Sitzung nie
$__timeout = (int) (get_setting('staff_session_timeout', '30') ?: 30);
$__last = (int) ($_SESSION['staff_last_activity'] ?? 0);
if ($__last && (time() - $__last) > $__timeout * 60) {
http_response_code(401);
echo json_encode(['error' => 'login', 'sessionExpired' => true]);
exit;
}
// Die Sitzungsdatei sofort freigeben: Der Abruf läuft alle paar Sekunden und soll andere Seitenaufrufe nicht ausbremsen
if (session_status() === PHP_SESSION_ACTIVE) session_write_close();
$pdo = get_db();
$since = max(0, (int) ($_GET['since'] ?? 0));
[$deptFilter, $deptParams] = dept_filter_sql(staff_department_ids($staff), 't.department_id');
$count = function (string $where, array $params = []) use ($pdo, $deptFilter, $deptParams): int {
$s = $pdo->prepare("SELECT COUNT(*) FROM tickets t WHERE {$where}{$deptFilter}");
$s->execute(array_merge($params, $deptParams));
return (int) $s->fetchColumn();
};
$open = "t.status NOT IN ('resolved','closed')";
$counts = [
'open' => $count($open),
'mine' => $count("t.assigned_staff_id = ? AND {$open}", [(int) $staff['id']]),
'unassigned' => $count("t.assigned_staff_id IS NULL AND {$open}"),
'overdue' => $count("{$open} AND (t.is_overdue = 1 OR (t.sla_paused_at IS NULL AND t.resolution_due_at IS NOT NULL AND t.resolution_due_at < ?))", [db_now()]),
'unanswered' => $count("t.status = 'open'"),
];
// Neue Tickets: ohne Spam, ohne Live-Chats (die haben eigene Hinweise) und ohne zusammengeführte
$visible = "t.is_spam = 0 AND t.source <> 'chat' AND t.merged_into IS NULL";
$m = $pdo->prepare("SELECT COALESCE(MAX(t.id), 0) FROM tickets t WHERE {$visible}{$deptFilter}");
$m->execute($deptParams);
$latest = (int) $m->fetchColumn();
$tickets = [];
$more = 0;
if ($since > 0 && $latest > $since) {
$s = $pdo->prepare("SELECT t.id, t.ticket_number, t.subject, t.requester_name, d.name AS department FROM tickets t LEFT JOIN departments d ON d.id = t.department_id
WHERE t.id > ? AND {$visible}{$deptFilter} ORDER BY t.id ASC LIMIT 6");
$s->execute(array_merge([$since], $deptParams));
foreach ($s->fetchAll() as $t) {
$tickets[] = ['id' => (int) $t['id'], 'number' => (string) $t['ticket_number'], 'subject' => mb_substr((string) $t['subject'], 0, 120), 'name' => mb_substr((string) $t['requester_name'], 0, 60), 'department' => (string) ($t['department'] ?? '')];
}
$c = $pdo->prepare("SELECT COUNT(*) FROM tickets t WHERE t.id > ? AND {$visible}{$deptFilter}");
$c->execute(array_merge([$since], $deptParams));
$more = max(0, (int) $c->fetchColumn() - count($tickets));
}
echo json_encode(['latest' => $latest, 'counts' => $counts, 'tickets' => $tickets, 'more' => $more], JSON_UNESCAPED_UNICODE);