Upload via GUI (278 Dateien)

This commit is contained in:
2026-10-21 16:44:49 +00:00
parent a94a36444c
commit 71eae4116a
6 changed files with 101 additions and 2 deletions
+33
View File
@@ -113,6 +113,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
redirect('/staff/profile.php?tab=settings');
}
// ── Android-Push: welche Popups, stille Zeiten ─────────────────
if ($action === 'save_push_prefs') {
$ev = array_map('strval', (array) ($_POST['push_events'] ?? []));
$qf = trim((string) ($_POST['quiet_from'] ?? ''));
$qt = trim((string) ($_POST['quiet_to'] ?? ''));
if (($qf !== '' || $qt !== '') && (!fcm_time_valid($qf) || !fcm_time_valid($qt))) {
flash('error', 'Stille Zeiten bitte als Uhrzeit von/bis angeben (z. B. 22:00 und 06:00).');
redirect('/staff/profile.php?tab=account');
}
fcm_save_prefs($pdo, (int) $staff['id'], $ev, $qf, $qt, array_keys(fcm_event_types_for($staff)));
flash('success', 'Push-Einstellungen gespeichert.');
redirect('/staff/profile.php?tab=account');
}
// ── 2FA aktivieren ─────────────────────────────────────────────
if ($action === 'enable_2fa') {
$code = trim($_POST['totp_code'] ?? '');
@@ -302,6 +316,25 @@ require __DIR__ . '/_header.php';
<?php endif; ?>
</div>
<?php $__pp = fcm_prefs($pdo, (int) $staff['id']); ?>
<div class="card">
<h3>📱 Android-App: Welche Popups?</h3>
<form method="post">
<?= csrf_field() ?>
<input type="hidden" name="action" value="save_push_prefs">
<p class="muted" style="font-size:.84rem;margin-bottom:10px">Wähle, wofür die App ein Popup zeigen soll.</p>
<?php foreach (fcm_event_types_for($staff) as $__k => $__label): ?>
<label style="display:flex;align-items:center;gap:8px;margin:6px 0;font-weight:400"><input type="checkbox" name="push_events[]" value="<?= e($__k) ?>" style="width:auto" <?= in_array($__k, $__pp['events'], true) ? 'checked' : '' ?>> <?= e($__label) ?></label>
<?php endforeach; ?>
<div style="margin-top:12px;font-size:.84rem;font-weight:600">Stille Zeiten (keine Popups)</div>
<div style="display:flex;align-items:center;gap:8px;margin-top:6px">
von <input type="time" name="quiet_from" value="<?= e($__pp['quiet_from']) ?>" style="width:auto"> bis <input type="time" name="quiet_to" value="<?= e($__pp['quiet_to']) ?>" style="width:auto">
</div>
<p class="muted" style="font-size:.78rem;margin-top:4px">Beide Felder leer = immer Popups. Die Zeit gilt nach der Serverzeit (<?= e(date_default_timezone_get()) ?>), auch über Mitternacht (z. B. 22:00 bis 06:00).</p>
<div class="btn-row"><button type="submit" class="btn">Speichern</button></div>
</form>
</div>
<!-- ══ EINSTELLUNGEN ══════════════════════════════════════════ -->
<?php elseif ($tab === 'settings'): ?>
+1
View File
@@ -354,6 +354,7 @@ $tsLabels = ['available'=>'Verfügbar','busy'=>'Beschäftigt','break'=>'Pause','
$teamStatus = [];
$myAvailStatus = 'available';
try {
staff_run_auto_offline($pdo);
$tsStmt = $pdo->query(
"SELECT s.id, s.name, s.is_on_vacation, sa.status AS avail_status, sa.note AS avail_note
FROM staff s
+2 -1
View File
@@ -34,8 +34,9 @@ if ($last && $elapsed > ($timeout * 60)) {
exit;
}
// Aktivität erneuern
// Aktivität erneuern (dieser Ping kommt nur bei echten Klicks, Tipps und Mausbewegungen)
$_SESSION['staff_last_activity'] = time();
staff_touch_activity(get_db(), (int) $_SESSION['staff_id']);
$remaining = $timeout * 60;
echo json_encode(['ok' => true, 'remaining' => $remaining]);
+63 -1
View File
@@ -350,6 +350,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
flash('success', 'Benachrichtigungseinstellungen gespeichert.');
redirect('/staff/settings.php?tab=benachrichtigungen');
}
// Push-Benachrichtigungen für die Android-App (Firebase Cloud Messaging)
if ($action === 'save_fcm') {
set_setting('fcm_enabled', !empty($_POST['fcm_enabled']) ? '1' : '0');
set_setting('fcm_show_details', !empty($_POST['fcm_show_details']) ? '1' : '0');
set_setting('push_brand', fcm_clean_brand((string) ($_POST['push_brand'] ?? '')));
flash('success', 'Einstellungen für die Android-Benachrichtigungen gespeichert.');
redirect('/staff/settings.php?tab=benachrichtigungen');
}
if ($action === 'remove_fcm') {
set_setting('fcm_service_account', '');
set_setting('fcm_access_token', '');
audit_log($staff['id'], 'Firebase-Dienstkonto für Android-Push entfernt');
flash('success', 'Der Firebase-Schlüssel wurde vom Server entfernt.');
redirect('/staff/settings.php?tab=benachrichtigungen');
}
if ($action === 'test_fcm') {
if (fcm_mode() === 'none') { flash('error', 'Push ist nicht eingerichtet: Es ist kein Push-Dienst eingetragen und kein eigener Firebase-Schlüssel hinterlegt.'); redirect('/staff/settings.php?tab=benachrichtigungen'); }
$t = fcm_send_test($pdo, (int) $staff['id']);
if ($t['devices'] === 0) { flash('error', 'Auf deinem Konto ist noch kein Android-Gerät angemeldet. Melde dich in der App an (Benachrichtigungen erlauben) und versuche es erneut.'); redirect('/staff/settings.php?tab=benachrichtigungen'); }
$t['sent'] > 0 ? flash('success', "Testnachricht an {$t['sent']} Gerät(e) gesendet. Sie sollte jetzt auf dem Handy erscheinen.") : flash('error', 'Senden fehlgeschlagen: ' . ($t['error'] !== '' ? $t['error'] : 'unbekannter Fehler'));
redirect('/staff/settings.php?tab=benachrichtigungen');
}
if ($action === 'test_notifications') {
$sent = [];
if (discord_notify('🔔 Testnachricht von DeskHub')) $sent[] = 'Discord';
@@ -842,7 +865,8 @@ require __DIR__ . '/_header.php';
<!-- ── TAB: BENACHRICHTIGUNGEN ────────────────────────────────────────────── -->
<?php elseif ($tab === 'benachrichtigungen'): ?>
<div class="card" style="max-width:640px">
<div style="display:flex;flex-wrap:wrap;gap:20px;align-items:flex-start">
<div class="card" style="max-width:640px;flex:1 1 420px;min-width:0">
<h3>Discord & Telegram</h3>
<p class="muted" style="font-size:.85rem;margin-bottom:16px">Mitarbeiter werden bei neuen Tickets und Antworten benachrichtigt. Ticket-Daten werden dabei an den jeweiligen Drittanbieter übermittelt.</p>
<p class="muted" style="font-size:.8rem;margin-bottom:12px;padding:8px 10px;background:var(--primary-lt);border-radius:6px;border:1px solid var(--border)">
@@ -868,6 +892,44 @@ require __DIR__ . '/_header.php';
</form>
</div>
<?php
$__fcmMode = fcm_mode();
$__fcmSa = fcm_service_account();
$__fcmDevices = (int) $pdo->query('SELECT COUNT(*) FROM push_devices')->fetchColumn();
$__fcmErr = (string) get_setting('fcm_last_error', '');
$__fcmOk = (string) get_setting('fcm_last_ok', '');
$__fcmFromConfig = (string) (config()['fcm']['service_account_file'] ?? '') !== '';
?>
<div class="card" style="max-width:640px;flex:1 1 420px;min-width:0">
<h3>📱 Android-App: Push-Benachrichtigungen</h3>
<p class="muted" style="font-size:.85rem;margin-bottom:12px">Popups auf dem Handy bei neuen Tickets, Kundenantworten und Zuweisungen, auch wenn die App geschlossen ist. Die Nachrichten laufen über Google (Firebase Cloud Messaging).</p>
<p style="margin:0 0 10px;font-size:.9rem">
<?php if ($__fcmMode === 'own'): ?>
<strong style="color:#15803d">✓ Eigenes Firebase-Projekt</strong> · <code><?= e($__fcmSa['project_id']) ?></code><?= $__fcmFromConfig ? ' (aus config/config.php)' : '' ?><br>
<?php elseif ($__fcmMode === 'relay'): ?>
<strong style="color:#15803d">✓ Push ist aktiv</strong> <span class="muted">· über den DeskHub-Push-Dienst, es ist nichts einzurichten</span><br>
<?php else: ?>
<strong style="color:#b45309">Nicht verfügbar.</strong> <span class="muted">In dieser Version ist kein Push-Dienst eingetragen. Ihr könnt unten ein eigenes Firebase-Projekt verwenden.</span><br>
<?php endif; ?>
<?php if ($__fcmMode !== 'none'): ?><span class="muted"><?= (int) $__fcmDevices ?> Gerät(e) angemeldet<?= $__fcmOk !== '' ? ' · letzter erfolgreicher Versand: ' . e($__fcmOk) : '' ?></span><?php endif; ?>
<?php if ($__fcmErr !== ''): ?><br><span style="color:#b91c1c;font-size:.85rem">Letzter Fehler: <?= e($__fcmErr) ?></span><?php endif; ?>
</p>
<form method="post">
<?= csrf_field() ?><input type="hidden" name="action" value="save_fcm">
<label style="display:flex;align-items:center;gap:8px;margin-top:0"><input type="checkbox" name="fcm_enabled" value="1" style="width:auto" <?= get_setting('fcm_enabled', '1') === '1' ? 'checked' : '' ?>> Push-Benachrichtigungen senden</label>
<label style="display:flex;align-items:flex-start;gap:8px;margin-top:10px"><input type="checkbox" name="fcm_show_details" value="1" style="width:auto;margin-top:3px" <?= get_setting('fcm_show_details', '0') === '1' ? 'checked' : '' ?>>
<span>Betreff und Kundenname in der Benachrichtigung anzeigen<br><span class="muted" style="font-size:.8rem">Aus (Standard): Es steht nur „Neues Ticket #T-123“ drin, Betreff und Kundenname verlassen euren Server nicht. An: Betreff und Name laufen dann über den Push-Dienst und die Server von Google.</span></span></label>
<label for="push_brand" style="margin-top:14px">Name in der Benachrichtigung (optional)</label>
<input type="text" id="push_brand" name="push_brand" maxlength="40" value="<?= e(fcm_brand()) ?>" placeholder="z. B. Windelgeschichten Support">
<p class="muted" style="font-size:.78rem;margin-top:4px">Erscheint in jedem Popup neben dem App-Namen („DeskHub“). Den App-Namen selbst kann Android pro Server nicht ändern.</p>
<div class="btn-row" style="gap:8px;margin-top:12px"><button type="submit" class="btn">Speichern</button></div>
</form>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px">
<form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="test_fcm"><button type="submit" class="btn secondary" <?= $__fcmMode !== 'none' ? '' : 'disabled' ?>>Testnachricht an meine Geräte</button></form>
<?php if ($__fcmSa && !$__fcmFromConfig): ?><form method="post" onsubmit="return confirm('Den eigenen Firebase-Schlüssel vom Server entfernen? Danach wird wieder der DeskHub-Push-Dienst verwendet.')"><?= csrf_field() ?><input type="hidden" name="action" value="remove_fcm"><button type="submit" class="btn secondary" style="border-color:var(--danger);color:var(--danger)">Eigenen Schlüssel entfernen</button></form><?php endif; ?>
</div>
</div>
</div>
<!-- ── TAB: UPLOADS ───────────────────────────────────────────────────────── -->
<?php elseif ($tab === 'uploads'): ?>
<div class="card" style="max-width:640px">
+1
View File
@@ -502,6 +502,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$agentName = '';
foreach ($agents as $a) { if ($a['id'] == $assignedTo) $agentName = $a['name']; }
log_activity($pdo, $ticket['id'], $staff['id'], "Mitarbeiter zugewiesen: {$agentName}");
fcm_notify_assignment($ticket, (int) $assignedTo, (int) $staff['id']); // Push auf die Android-App des neuen Bearbeiters
} elseif ($assignedTo === null && !empty($ticket['assigned_staff_id'])) {
log_activity($pdo, $ticket['id'], $staff['id'], 'Zuweisung entfernt');
}
+1
View File
@@ -98,6 +98,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['bulk_action'] ?? '') !== '
if ($bulkAction === 'assign') {
$newAgent = $_POST['bulk_assigned_staff_id'] !== '' ? (int) $_POST['bulk_assigned_staff_id'] : null;
$pdo->prepare('UPDATE tickets SET assigned_staff_id=?, updated_at=? WHERE id=?')->execute([$newAgent, $now, $t['id']]);
if ($newAgent !== null && $newAgent !== (int) ($t['assigned_staff_id'] ?? 0)) fcm_notify_assignment($t, $newAgent, (int) $staff['id']); // Push auf die Android-App
log_activity($pdo, $t['id'], $staff['id'], 'Sammelaktion: Mitarbeiter zugewiesen');
} elseif ($bulkAction === 'priority') {
$newPrio = (int) $_POST['bulk_priority_id'];