'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);