226 lines
10 KiB
PHP
226 lines
10 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||
$staff = require_staff_login();
|
||
$pdo = get_db();
|
||
|
||
$userId = (int) ($_GET['id'] ?? 0);
|
||
$guestEmail = trim($_GET['email'] ?? '');
|
||
$isGuest = false;
|
||
|
||
if ($userId > 0) {
|
||
$stmt = $pdo->prepare('SELECT u.*, o.name AS org_name FROM users u LEFT JOIN organizations o ON o.id = u.organization_id WHERE u.id = ?');
|
||
$stmt->execute([$userId]);
|
||
$customer = $stmt->fetch();
|
||
} elseif ($guestEmail !== '') {
|
||
// Gast-Anfrager (kein Konto, nur über Tickets bekannt) – keine Zeile in "users",
|
||
// daher hier aus den Ticket-Daten zusammengesetzt statt per user_id nachgeschlagen.
|
||
$stmt = $pdo->prepare(
|
||
"SELECT requester_email AS email, MAX(requester_name) AS name, MIN(created_at) AS created_at,
|
||
NULL AS phone, NULL AS org_name
|
||
FROM tickets WHERE requester_email = ? AND user_id IS NULL GROUP BY requester_email"
|
||
);
|
||
$stmt->execute([$guestEmail]);
|
||
$customer = $stmt->fetch();
|
||
$isGuest = (bool) $customer;
|
||
} else {
|
||
$customer = false;
|
||
}
|
||
|
||
if (!$customer) {
|
||
flash('error', 'Kunde nicht gefunden.');
|
||
redirect('/staff/customers.php');
|
||
}
|
||
|
||
$errors = [];
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
$action = $_POST['action'] ?? '';
|
||
|
||
// Notizen und Kontaktdaten hängen an einem echten Kundenkonto (users-Tabelle) –
|
||
// für Gast-Anfrager ohne Konto gibt es das nicht, siehe Hinweis oben in der Seite.
|
||
if ($isGuest && in_array($action, ['add_note', 'delete_note', 'save_customer'], true)) {
|
||
flash('error', 'Für Gast-Anfrager ohne Konto nicht verfügbar.');
|
||
redirect('/staff/customer_profile.php?email=' . urlencode($guestEmail));
|
||
}
|
||
|
||
if ($action === 'add_note') {
|
||
$note = trim($_POST['note'] ?? '');
|
||
if ($note !== '') {
|
||
$pdo->prepare('INSERT INTO customer_notes (user_id, staff_id, note, created_at) VALUES (?,?,?,?)')
|
||
->execute([$userId, $staff['id'], $note, db_now()]);
|
||
flash('success', 'Notiz gespeichert.');
|
||
}
|
||
redirect('/staff/customer_profile.php?id=' . $userId);
|
||
}
|
||
|
||
if ($action === 'delete_note') {
|
||
$noteId = (int) ($_POST['note_id'] ?? 0);
|
||
$pdo->prepare('DELETE FROM customer_notes WHERE id=? AND user_id=?')->execute([$noteId, $userId]);
|
||
redirect('/staff/customer_profile.php?id=' . $userId);
|
||
}
|
||
|
||
if ($action === 'save_customer') {
|
||
$name = trim($_POST['name'] ?? '');
|
||
$phone = trim($_POST['phone'] ?? '');
|
||
if ($name !== '') {
|
||
$pdo->prepare('UPDATE users SET name=?, phone=? WHERE id=?')->execute([$name, $phone ?: null, $userId]);
|
||
flash('success', 'Kundendaten gespeichert.');
|
||
}
|
||
redirect('/staff/customer_profile.php?id=' . $userId);
|
||
}
|
||
}
|
||
|
||
// Alle Tickets dieses Kunden (Mitarbeiter sehen nur Tickets ihrer Abteilungen)
|
||
[$deptSql, $deptParams] = dept_filter_sql(staff_department_ids($staff), 't.department_id');
|
||
$ticketStmt = $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.requester_email = ? OR t.user_id = ?) {$deptSql}
|
||
ORDER BY t.created_at DESC LIMIT 100"
|
||
);
|
||
$ticketStmt->execute([$customer['email'], $userId, ...$deptParams]);
|
||
$tickets = $ticketStmt->fetchAll();
|
||
|
||
// Notizen
|
||
$noteStmt = $pdo->prepare(
|
||
"SELECT cn.*, COALESCE(s.name, 'Gelöschter Mitarbeiter') AS staff_name
|
||
FROM customer_notes cn
|
||
LEFT JOIN staff s ON s.id = cn.staff_id
|
||
WHERE cn.user_id = ? ORDER BY cn.created_at DESC"
|
||
);
|
||
$noteStmt->execute([$userId]);
|
||
$notes = $noteStmt->fetchAll();
|
||
|
||
// Statistiken
|
||
$totalTickets = count($tickets);
|
||
$openTickets = count(array_filter($tickets, fn($t) => !in_array($t['status'], ['resolved','closed'])));
|
||
$closedTickets = count(array_filter($tickets, fn($t) => in_array($t['status'], ['resolved','closed'])));
|
||
|
||
$pageTitle = 'Kundenprofil: ' . $customer['name'];
|
||
$activePage = 'customers';
|
||
require __DIR__ . '/_header.php';
|
||
?>
|
||
|
||
<div class="topbar">
|
||
<div>
|
||
<h1><?= e($customer['name']) ?> <?php if ($isGuest): ?><span class="badge" style="background:#64748b; font-size:.7rem; vertical-align:middle">Gast</span><?php endif; ?></h1>
|
||
<span class="muted" style="font-size:.88rem"><?= e($customer['email']) ?></span>
|
||
</div>
|
||
<a href="/staff/customers.php" class="btn secondary">← Zurück</a>
|
||
</div>
|
||
<?php if ($isGuest): ?>
|
||
<div class="flash info" style="margin-bottom:20px">Gast-Anfrager ohne eigenes Kundenkonto – Notizen und Kontaktdaten-Bearbeitung sind daher nicht verfügbar.</div>
|
||
<?php endif; ?>
|
||
|
||
<div style="display:flex; gap:24px; align-items:flex-start; flex-wrap:wrap">
|
||
|
||
<!-- Linke Spalte -->
|
||
<div style="flex:2; min-width:0">
|
||
|
||
<!-- Tickets -->
|
||
<div class="card">
|
||
<h3>Tickets (<?= $totalTickets ?>)</h3>
|
||
<?php if (!$tickets): ?>
|
||
<div class="empty-state">Keine Tickets vorhanden.</div>
|
||
<?php else: ?>
|
||
<table>
|
||
<thead><tr><th>Ticket</th><th>Betreff</th><th>Status</th><th>Priorität</th><th>Mitarbeiter</th><th>Erstellt</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($tickets as $t):
|
||
[$label, $color] = status_label($t['status']);
|
||
?>
|
||
<tr>
|
||
<td><a href="/staff/ticket_view.php?n=<?= urlencode($t['ticket_number']) ?>" class="ticket-no">#<?= e($t['ticket_number']) ?></a></td>
|
||
<td><a href="/staff/ticket_view.php?n=<?= urlencode($t['ticket_number']) ?>"><?= e($t['subject']) ?></a></td>
|
||
<td><span class="badge" style="background:<?= $color ?>"><?= e($label) ?></span></td>
|
||
<td><span class="badge" style="background:<?= e($t['priority_color']) ?>"><?= e($t['priority_name']) ?></span></td>
|
||
<td class="muted"><?= e($t['staff_name'] ?? '–') ?></td>
|
||
<td class="muted"><?= format_dt($t['created_at']) ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<!-- Notizen -->
|
||
<?php if (!$isGuest): ?>
|
||
<div class="card">
|
||
<h3>Interne Notizen</h3>
|
||
<form method="post" style="margin-bottom:16px">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="add_note">
|
||
<textarea name="note" placeholder="Notiz hinzufügen…" rows="3" required></textarea>
|
||
<div class="btn-row"><button type="submit" class="btn">Notiz speichern</button></div>
|
||
</form>
|
||
<?php if (!$notes): ?>
|
||
<p class="muted">Noch keine Notizen.</p>
|
||
<?php else: ?>
|
||
<?php foreach ($notes as $n): ?>
|
||
<div style="border-bottom:1px solid var(--border-soft); padding:10px 0; display:flex; justify-content:space-between; gap:12px">
|
||
<div>
|
||
<div style="font-size:.82rem; color:var(--muted); margin-bottom:4px"><?= e($n['staff_name']) ?> · <?= format_dt($n['created_at']) ?></div>
|
||
<div style="white-space:pre-wrap"><?= e($n['note']) ?></div>
|
||
</div>
|
||
<form method="post" onsubmit="return confirm('Notiz löschen?')">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="delete_note">
|
||
<input type="hidden" name="note_id" value="<?= $n['id'] ?>">
|
||
<button type="submit" style="background:none;border:none;color:var(--muted-lt);cursor:pointer;font-size:.9rem" title="Löschen">✕</button>
|
||
</form>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
</div>
|
||
|
||
<!-- Rechte Spalte -->
|
||
<div style="flex:1; min-width:240px; max-width:280px">
|
||
|
||
<!-- Statistiken -->
|
||
<div class="stat-grid" style="grid-template-columns:1fr 1fr; margin-bottom:16px">
|
||
<div class="stat-card"><div class="num"><?= $totalTickets ?></div><div class="lbl">Gesamt</div></div>
|
||
<div class="stat-card"><div class="num"><?= $openTickets ?></div><div class="lbl">Offen</div></div>
|
||
<div class="stat-card"><div class="num"><?= $closedTickets ?></div><div class="lbl">Gelöst</div></div>
|
||
<div class="stat-card"><div class="num"><?= format_dt($customer['created_at'], 'd.m.Y') ?></div><div class="lbl">Seit</div></div>
|
||
</div>
|
||
|
||
<!-- Kundendaten bearbeiten -->
|
||
<div class="card" style="padding:16px 18px">
|
||
<div style="font-size:.78rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-bottom:12px">Kontaktdaten</div>
|
||
<?php if ($isGuest): ?>
|
||
<label style="font-size:.78rem; color:var(--muted)">Name</label>
|
||
<div style="font-size:.84rem; padding:7px 0"><?= e($customer['name']) ?></div>
|
||
<label style="font-size:.78rem; margin-top:4px; color:var(--muted)">E-Mail</label>
|
||
<div style="font-size:.84rem; padding:7px 0; color:var(--muted)"><?= e($customer['email']) ?></div>
|
||
<p class="muted" style="font-size:.78rem; margin-top:8px">Ohne Konto nicht bearbeitbar.</p>
|
||
<?php else: ?>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="save_customer">
|
||
<label style="font-size:.78rem">Name</label>
|
||
<input type="text" name="name" value="<?= e($customer['name']) ?>" required>
|
||
<label style="font-size:.78rem; margin-top:8px">Telefon</label>
|
||
<input type="text" name="phone" value="<?= e($customer['phone'] ?? '') ?>" placeholder="Optional">
|
||
<label style="font-size:.78rem; margin-top:8px; color:var(--muted)">E-Mail</label>
|
||
<div style="font-size:.84rem; padding:7px 0; color:var(--muted)"><?= e($customer['email']) ?></div>
|
||
<?php if ($customer['org_name']): ?>
|
||
<label style="font-size:.78rem; margin-top:4px; color:var(--muted)">Organisation</label>
|
||
<div style="font-size:.84rem; padding:7px 0"><?= e($customer['org_name']) ?></div>
|
||
<?php endif; ?>
|
||
<button type="submit" class="btn" style="width:100%; margin-top:12px; justify-content:center">Speichern</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<?php require __DIR__ . '/_footer.php'; ?>
|