Files
2026-11-04 07:55:30 +00:00

172 lines
7.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
/**
* staff/ticket_print.php
* Saubere Druckansicht eines Tickets – ohne Navigation, optimiert für Druck/PDF.
*/
require_once __DIR__ . '/../includes/bootstrap.php';
$staff = require_staff_login();
$pdo = get_db();
$number = $_GET['n'] ?? '';
$stmt = $pdo->prepare(
'SELECT t.*, p.name AS priority_name, p.color AS priority_color,
d.name AS department_name, s.name AS staff_name
FROM tickets t
JOIN priorities p ON p.id = t.priority_id
JOIN departments d ON d.id = t.department_id
LEFT JOIN staff s ON s.id = t.assigned_staff_id
WHERE t.ticket_number = ?'
);
$stmt->execute([$number]);
$ticket = $stmt->fetch();
if (!$ticket) {
http_response_code(404);
die('Ticket nicht gefunden.');
}
if (!staff_can_access_department($staff, (int) $ticket['department_id'])) {
http_response_code(403);
die('Sie haben keinen Zugriff auf Tickets dieser Abteilung.');
}
// Thread laden
$thread = $pdo->prepare(
'SELECT tt.*, s.name AS staff_name, u.name AS user_name
FROM ticket_threads tt
LEFT JOIN staff s ON s.id = tt.staff_id
LEFT JOIN users u ON u.id = tt.user_id
WHERE tt.ticket_id = ? AND tt.is_internal_note = 0
ORDER BY tt.created_at ASC'
);
$thread->execute([$ticket['id']]);
$messages = $thread->fetchAll();
// Anhänge
$attStmt = $pdo->prepare('SELECT * FROM ticket_attachments WHERE thread_id = ?');
$siteName = get_setting('display_name', config()['site']['name'] ?? 'DeskHub');
[$statusLabel] = status_label($ticket['status']);
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Ticket #<?= e($ticket['ticket_number']) ?> – <?= e($ticket['subject']) ?></title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 11pt; color: #1e293b; background: #fff; padding: 20mm; }
.print-header { display: flex; justify-content: space-between; align-items: flex-start; padding-bottom: 12px; border-bottom: 2px solid #0f6f6a; margin-bottom: 16px; }
.print-header .logo { font-size: 18pt; font-weight: 800; color: #0f6f6a; }
.print-header .meta { text-align: right; font-size: 9pt; color: #64748b; }
.ticket-title { font-size: 16pt; font-weight: 700; margin-bottom: 12px; }
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 24px; margin-bottom: 20px; padding: 12px 16px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; }
.info-row { display: flex; gap: 8px; font-size: 9.5pt; }
.info-label { font-weight: 600; color: #64748b; min-width: 100px; }
.info-value { color: #1e293b; }
.section-title { font-size: 10pt; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: #64748b; margin: 20px 0 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px; }
.message { border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 12px; break-inside: avoid; }
.message-header { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; background: #f8fafc; border-bottom: 1px solid #e2e8f0; font-size: 9pt; }
.message-header .author { font-weight: 600; }
.message-header .date { color: #64748b; }
.message-header.staff-header { background: #e6f4f3; }
.message-body { padding: 10px 12px; font-size: 10pt; line-height: 1.6; }
.attachments { padding: 6px 12px 8px; border-top: 1px solid #e2e8f0; font-size: 9pt; color: #64748b; }
.print-footer { margin-top: 24px; padding-top: 10px; border-top: 1px solid #e2e8f0; font-size: 8.5pt; color: #94a3b8; display: flex; justify-content: space-between; }
.no-print { }
@media print {
body { padding: 10mm; }
.no-print { display: none !important; }
@page { margin: 15mm; }
}
</style>
<?= site_icon_links() ?>
</head>
<body>
<!-- Drucken-Button (nur am Bildschirm) -->
<div class="no-print" style="position:fixed;top:16px;right:16px;display:flex;gap:8px;z-index:99">
<button onclick="window.print()" style="padding:8px 18px;background:#0f6f6a;color:#fff;border:none;border-radius:6px;cursor:pointer;font-size:.88rem;font-weight:600">🖨 Drucken / Als PDF speichern</button>
<a href="/staff/ticket_view.php?n=<?= urlencode($number) ?>" style="padding:8px 16px;border:1px solid #e2e8f0;border-radius:6px;text-decoration:none;color:#64748b;font-size:.88rem;background:#fff">← Zurück</a>
</div>
<!-- Kopfzeile -->
<div class="print-header">
<div class="logo"><?= e($siteName) ?></div>
<div class="meta">
Gedruckt: <?= date('d.m.Y H:i') ?><br>
von: <?= e($staff['name']) ?>
</div>
</div>
<!-- Ticket-Titel -->
<div class="ticket-title"><?= e($ticket['subject']) ?></div>
<!-- Ticket-Info -->
<div class="info-grid">
<div class="info-row"><span class="info-label">Ticket-Nr.:</span><span class="info-value">#<?= e($ticket['ticket_number']) ?></span></div>
<div class="info-row"><span class="info-label">Status:</span><span class="info-value"><?= e($statusLabel) ?></span></div>
<div class="info-row"><span class="info-label">Absender:</span><span class="info-value"><?= e($ticket['requester_name']) ?> &lt;<?= e($ticket['requester_email']) ?>&gt;</span></div>
<div class="info-row"><span class="info-label">Priorität:</span><span class="info-value"><?= e($ticket['priority_name']) ?></span></div>
<div class="info-row"><span class="info-label">Abteilung:</span><span class="info-value"><?= e($ticket['department_name']) ?></span></div>
<div class="info-row"><span class="info-label">Zugewiesen:</span><span class="info-value"><?= e($ticket['staff_name'] ?? '– nicht zugewiesen –') ?></span></div>
<div class="info-row"><span class="info-label">Erstellt:</span><span class="info-value"><?= date('d.m.Y H:i', strtotime($ticket['created_at'])) ?></span></div>
<?php if ($ticket['resolution_due_at']): ?>
<div class="info-row"><span class="info-label">Fällig bis:</span><span class="info-value"><?= date('d.m.Y H:i', strtotime($ticket['resolution_due_at'])) ?></span></div>
<?php endif; ?>
</div>
<!-- Nachrichten -->
<div class="section-title">Verlauf (<?= count($messages) ?> Nachricht<?= count($messages) !== 1 ? 'en' : '' ?>)</div>
<?php foreach ($messages as $msg):
$isStaff = $msg['author_type'] === 'staff';
$author = $isStaff ? ($msg['staff_name'] ?? 'Mitarbeiter') : ($msg['user_name'] ?? $ticket['requester_name']);
$body = $msg['message'] ?? '';
$isHtml = message_is_html($body);
$attStmt->execute([$msg['id']]);
$atts = $attStmt->fetchAll();
?>
<div class="message">
<div class="message-header <?= $isStaff ? 'staff-header' : '' ?>">
<span class="author"><?= e($author) ?><?= $isStaff ? ' (Mitarbeiter)' : ' (Kunde)' ?></span>
<span class="date"><?= date('d.m.Y H:i', strtotime($msg['created_at'])) ?></span>
</div>
<div class="message-body">
<?php if ($isHtml): ?>
<?= sanitize_staff_html($body) ?>
<?php else: ?>
<?php
$text = trim($body);
foreach (preg_split('/\n{2,}/', $text) as $p) {
$p = trim($p);
if ($p !== '') echo '<p style="margin:0 0 8px">'.nl2br(e($p)).'</p>';
}
?>
<?php endif; ?>
</div>
<?php if ($atts): ?>
<div class="attachments">
📎 Anhänge: <?= implode(', ', array_map(fn($a) => e($a['original_name']), $atts)) ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<!-- Fußzeile -->
<div class="print-footer">
<span><?= e($siteName) ?> – Ticket #<?= e($ticket['ticket_number']) ?></span>
<span>Druckdatum: <?= date('d.m.Y H:i') ?></span>
</div>
</body>
</html>