From c7a2f19ac72677f256c547e0387b02775f448d88 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Fri, 23 Oct 2026 17:50:17 +0000 Subject: [PATCH] Upload via GUI (304 Dateien) --- DeskHub-Webseite/staff/assistant.php | 2 +- DeskHub-Webseite/staff/chat.php | 101 +++++++++++++++++++-------- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/DeskHub-Webseite/staff/assistant.php b/DeskHub-Webseite/staff/assistant.php index 66a714fa..1f398724 100644 --- a/DeskHub-Webseite/staff/assistant.php +++ b/DeskHub-Webseite/staff/assistant.php @@ -320,7 +320,7 @@ $tabs = ['settings' => 'Einstellungen', 'answers' => 'Zusätzliche Antworten' . $label): ?> diff --git a/DeskHub-Webseite/staff/chat.php b/DeskHub-Webseite/staff/chat.php index fc07af9f..fdffc469 100644 --- a/DeskHub-Webseite/staff/chat.php +++ b/DeskHub-Webseite/staff/chat.php @@ -60,21 +60,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { redirect('/staff/chat.php?with=' . $toId); } +function chatAvClass(string $name): string { + $l = strtolower(mb_substr($name, 0, 1)); + return 'av-' . (ctype_alpha($l) ? $l : 'a'); +} + // AJAX: neue Nachrichten pollen if (($_GET['action'] ?? '') === 'poll' && $withId) { header('Content-Type: application/json'); - $since = $_GET['since'] ?? '1970-01-01 00:00:00'; + $after = max(0, (int) ($_GET['after'] ?? 0)); $pStmt = $pdo->prepare( "SELECT m.*, sf.name AS from_name FROM staff_chat_messages m JOIN staff sf ON sf.id = m.from_staff_id WHERE ((m.from_staff_id=? AND m.to_staff_id=?) OR (m.from_staff_id=? AND m.to_staff_id=?)) - AND m.created_at > ? - ORDER BY m.created_at ASC" + AND m.id > ? + ORDER BY m.id ASC" ); - $pStmt->execute([$staff['id'], $withId, $withId, $staff['id'], $since]); + $pStmt->execute([$staff['id'], $withId, $withId, $staff['id'], $after]); $pdo->prepare('UPDATE staff_chat_messages SET is_read=1 WHERE from_staff_id=? AND to_staff_id=?') ->execute([$withId, $staff['id']]); - echo json_encode($pStmt->fetchAll()); + // Anzeige-Angaben gleich mitliefern: Das Gerüst im Browser baut die Blase genau wie die Seite selbst + echo json_encode(array_map(fn(array $m) => [ + 'id' => (int) $m['id'], 'from_staff_id' => (int) $m['from_staff_id'], 'from_name' => $m['from_name'], 'message' => $m['message'], + 'created_at' => $m['created_at'], 'initial' => mb_strtoupper(mb_substr($m['from_name'], 0, 1)), 'av_class' => chatAvClass($m['from_name']), + ], $pStmt->fetchAll())); exit; } @@ -138,10 +147,6 @@ require __DIR__ . '/_header.php';
@@ -173,7 +178,7 @@ function chatAvClass(string $name): string {
-
Noch keine Nachrichten – schreib etwas!
+
Noch keine Nachrichten – schreib etwas!
-
+
@@ -254,7 +259,7 @@ var withId = ; var csrfToken = document.querySelector('[name=csrf_token]').value; // Token nach Rotation aktualisieren function getCsrf() { return document.querySelector('[name=csrf_token]').value; } -var lastTs = ; +var lastId = ; function insertEmoji(em) { var input = document.getElementById('chat-input'); @@ -279,14 +284,52 @@ function scrollBottom() { scrollBottom(); function appendBubble(msg) { + var box = document.getElementById('chat-messages'); + if (box.querySelector('[data-id="' + msg.id + '"]')) return; // schon da + var empty = document.getElementById('chat-empty'); + if (empty) empty.remove(); var isMe = msg.from_staff_id == myId; - var div = document.createElement('div'); - div.className = 'chat-bubble ' + (isMe ? 'me' : 'them'); - div.dataset.ts = msg.created_at; - div.innerHTML = '
' + msg.message.replace(/&/g,'&').replace(/') + '
' - + '
' + msg.created_at + '
'; - document.getElementById('chat-messages').appendChild(div); - lastTs = msg.created_at; + var all = box.querySelectorAll('.ac-msg'); + var last = all.length ? all[all.length - 1] : null; + var day = msg.created_at.slice(0, 10); + if (!last || last.dataset.ts.slice(0, 10) !== day) { + var sep = document.createElement('div'); + sep.style.cssText = 'text-align:center;font-size:.72rem;color:#9ca3af;padding:12px 0'; + sep.textContent = day.slice(8, 10) + '.' + day.slice(5, 7) + '.' + day.slice(0, 4); + box.appendChild(sep); + } + var row = document.createElement('div'); + row.className = 'ac-msg ' + (isMe ? 'me' : 'them'); + row.dataset.id = msg.id; + row.dataset.ts = msg.created_at; + if (!isMe) { + var av = document.createElement('div'); + av.className = 'ac-msg-av ' + msg.av_class; + av.textContent = msg.initial; + row.appendChild(av); + } + var body = document.createElement('div'); + body.className = 'ac-msg-body'; + if (!isMe) { + var name = document.createElement('div'); + name.className = 'ac-msg-name'; + name.textContent = msg.from_name; + body.appendChild(name); + } + var bubble = document.createElement('div'); + bubble.className = 'ac-msg-bubble'; + msg.message.split('\n').forEach(function (line, i) { + if (i) bubble.appendChild(document.createElement('br')); + bubble.appendChild(document.createTextNode(line)); + }); + body.appendChild(bubble); + var time = document.createElement('div'); + time.className = 'ac-msg-time'; + time.textContent = msg.created_at.slice(11, 16); + body.appendChild(time); + row.appendChild(body); + box.appendChild(row); + if (msg.id > lastId) lastId = msg.id; scrollBottom(); } @@ -300,23 +343,19 @@ function sendMsg() { method: 'POST', headers: {'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'}, body: 'csrf_token=' + encodeURIComponent(getCsrf()) + '&to_id=' + withId + '&message=' + encodeURIComponent(msg) - }).then(function() { - appendBubble({from_staff_id: myId, message: msg, created_at: new Date().toISOString().slice(0,19).replace('T',' ')}); - poll(); - }); + }).then(function() { poll(); }); // die eigene Nachricht kommt mit Nummer und Serverzeit zurück } +var polling = false; function poll() { - fetch('/staff/chat.php?with=' + withId + '&action=poll&since=' + encodeURIComponent(lastTs)) + if (polling) return; + polling = true; + fetch('/staff/chat.php?with=' + withId + '&action=poll&after=' + lastId) .then(function(r){ return r.json(); }) - .then(function(msgs) { - msgs.forEach(function(m) { - if (m.from_staff_id != myId) appendBubble(m); - lastTs = m.created_at; - }); - }); + .then(function(msgs) { msgs.forEach(appendBubble); }) + .catch(function() {}) + .then(function() { polling = false; }); } - setInterval(poll, 8000);