Upload via GUI (304 Dateien)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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,'&').replace(/</g,'<').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; ?>
|
||||
|
||||
Reference in New Issue
Block a user