Upload via GUI (304 Dateien)

This commit is contained in:
2026-10-23 17:50:17 +00:00
parent 0d7e6535cd
commit c7a2f19ac7
2 changed files with 71 additions and 32 deletions
+1 -1
View File
@@ -320,7 +320,7 @@ $tabs = ['settings' => 'Einstellungen', 'answers' => 'Zusätzliche Antworten' .
<?php foreach ($logResults as $key => $label): ?><option value="<?= e($key) ?>" <?= $logFilter['result'] === $key ? 'selected' : '' ?>><?= e($label) ?></option><?php endforeach; ?>
</select>
<select name="lp" style="width:auto">
<?php foreach (['today' => 'Heute', '7' => 'Letzte 7 Tage', '30' => 'Letzte 30 Tage', 'all' => 'Gesamter Zeitraum'] as $k => $l): ?><option value="<?= e($k) ?>" <?= $logFilter['period'] === $k ? 'selected' : '' ?>><?= e($l) ?></option><?php endforeach; ?>
<?php foreach (['today' => 'Heute', '7' => 'Letzte 7 Tage', '30' => 'Letzte 30 Tage', 'all' => 'Gesamter Zeitraum'] as $k => $l): ?><option value="<?= e($k) ?>" <?= $logFilter['period'] === (string) $k ? 'selected' : '' ?>><?= e($l) ?></option><?php endforeach; ?>
</select>
<input type="text" name="lq" value="<?= e($logFilter['q']) ?>" placeholder="Suchen…" maxlength="100" style="flex:1;min-width:160px">
<button type="submit" class="btn secondary">Filtern</button>
+70 -31
View File
@@ -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';
</style>
<?php
function chatAvClass(string $name): string {
$l = strtolower(mb_substr($name, 0, 1));
return 'av-' . (ctype_alpha($l) ? $l : 'a');
}
?>
<div class="ac-wrap">
@@ -173,7 +178,7 @@ function chatAvClass(string $name): string {
<!-- Nachrichten -->
<div id="chat-messages" class="ac-msgs">
<?php if (!$conversation): ?>
<div style="text-align:center;color:#9ca3af;font-size:.82rem;padding:30px 0;margin:auto">Noch keine Nachrichten – schreib etwas!</div>
<div id="chat-empty" style="text-align:center;color:#9ca3af;font-size:.82rem;padding:30px 0;margin:auto">Noch keine Nachrichten – schreib etwas!</div>
<?php endif; ?>
<?php
$prevDate = '';
@@ -187,7 +192,7 @@ function chatAvClass(string $name): string {
?>
<div style="text-align:center;font-size:.72rem;color:#9ca3af;padding:12px 0"><?= date('d.m.Y', strtotime($date)) ?></div>
<?php endif; ?>
<div class="ac-msg <?= $isMe?'me':'them' ?>" data-ts="<?= e($msg['created_at']) ?>">
<div class="ac-msg <?= $isMe?'me':'them' ?>" data-id="<?= (int) $msg['id'] ?>" data-ts="<?= e($msg['created_at']) ?>">
<?php if (!$isMe): ?>
<div class="ac-msg-av <?= $cls ?>"><?= e($ini) ?></div>
<?php endif; ?>
@@ -254,7 +259,7 @@ var withId = <?= $withId ?>;
var csrfToken = document.querySelector('[name=csrf_token]').value;
// Token nach Rotation aktualisieren
function getCsrf() { return document.querySelector('[name=csrf_token]').value; }
var lastTs = <?= $conversation ? json_encode(end($conversation)['created_at']) : json_encode('1970-01-01 00:00:00') ?>;
var lastId = <?= $conversation ? (int) end($conversation)['id'] : 0 ?>;
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 = '<div class="bubble-text">' + msg.message.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/\n/g,'<br>') + '</div>'
+ '<div class="bubble-time">' + msg.created_at + '</div>';
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);
</script>
<?php endif; ?>