Merge branch 'main' into codex/lese-chat-programm-und-warte-auf-instruktionen-t4t8ra

This commit is contained in:
2025-11-03 17:09:14 +01:00
committed by GitHub
+67 -154
View File
@@ -1469,23 +1469,10 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
} }
:root {
--sun-900: #8c5e00;
--sun-700: #b77900;
--sun-600: #d18c00;
--sun-500: #f0b400;
--sun-400: #ffd046;
--sun-200: #ffe7a3;
--sun-100: #fff2c9;
--sun-50: #fff8e6;
--text-dark: #3c2a00;
}
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background: radial-gradient(circle at top, var(--sun-400) 0%, var(--sun-700) 45%, var(--sun-900) 100%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: var(--text-dark);
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1507,14 +1494,14 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
} }
.auth-container h1 { .auth-container h1 {
color: var(--sun-700); color: #667eea;
margin-bottom: 10px; margin-bottom: 10px;
font-size: 32px; font-size: 32px;
text-align: center; text-align: center;
} }
.auth-container .subtitle { .auth-container .subtitle {
color: rgba(60, 42, 0, 0.75); color: #666;
margin-bottom: 30px; margin-bottom: 30px;
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
@@ -1547,27 +1534,26 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
.auth-container label { .auth-container label {
display: block; display: block;
color: var(--text-dark); color: #333;
font-weight: 600; font-weight: 600;
margin-bottom: 8px; margin-bottom: 8px;
font-size: 14px; font-size: 14px;
} }
.auth-container input[type="text"], .auth-container input[type="text"],
.auth-container input[type="date"], .auth-container input[type="date"],
.auth-container input[type="password"] { .auth-container input[type="password"] {
width: 100%; width: 100%;
padding: 12px 15px; padding: 12px 15px;
border: 2px solid rgba(188, 118, 0, 0.2); border: 2px solid #e0e0e0;
border-radius: 10px; border-radius: 10px;
font-size: 15px; font-size: 15px;
transition: border-color 0.2s ease, box-shadow 0.2s ease; transition: border-color 0.3s;
} }
.auth-container input:focus { .auth-container input:focus {
outline: none; outline: none;
border-color: var(--sun-600); border-color: #667eea;
box-shadow: 0 0 0 3px rgba(240, 180, 0, 0.18);
} }
.auth-container .checkbox-group { .auth-container .checkbox-group {
@@ -1606,26 +1592,23 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
.auth-container button { .auth-container button {
width: 100%; width: 100%;
padding: 15px; padding: 15px;
background: linear-gradient(135deg, var(--sun-500) 0%, var(--sun-700) 100%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; color: white;
border: none; border: none;
border-radius: 12px; border-radius: 10px;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: bold;
cursor: pointer; cursor: pointer;
box-shadow: 0 12px 24px rgba(188, 118, 0, 0.35); transition: transform 0.2s;
transition: transform 0.2s ease, box-shadow 0.2s ease;
} }
.auth-container button:hover { .auth-container button:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 16px 28px rgba(188, 118, 0, 0.4);
} }
.auth-container button:disabled { .auth-container button:disabled {
opacity: 0.55; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
box-shadow: none;
} }
/* ═══════════════════════════════════════════════════════════ */ /* ═══════════════════════════════════════════════════════════ */
@@ -1645,7 +1628,7 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
text-align: center; text-align: center;
font-size: 28px; font-size: 28px;
margin-bottom: 10px; margin-bottom: 10px;
color: var(--sun-700); color: #4c51bf;
} }
.admin-login-container p { .admin-login-container p {
@@ -1669,34 +1652,31 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
width: 100%; width: 100%;
padding: 12px 14px; padding: 12px 14px;
border-radius: 10px; border-radius: 10px;
border: 2px solid rgba(188, 118, 0, 0.2); border: 2px solid #e0e0e0;
font-size: 15px; font-size: 15px;
transition: border-color 0.2s ease, box-shadow 0.2s ease; transition: border-color 0.2s ease;
} }
.admin-login-container input:focus { .admin-login-container input:focus {
outline: none; outline: none;
border-color: var(--sun-600); border-color: #667eea;
box-shadow: 0 0 0 3px rgba(240, 180, 0, 0.18);
} }
.admin-login-container button { .admin-login-container button {
width: 100%; width: 100%;
padding: 14px; padding: 14px;
border: none; border: none;
border-radius: 12px; border-radius: 10px;
background: linear-gradient(135deg, var(--sun-500) 0%, var(--sun-700) 100%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; color: white;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: bold;
cursor: pointer; cursor: pointer;
box-shadow: 0 12px 24px rgba(188, 118, 0, 0.35); transition: transform 0.2s ease;
transition: transform 0.2s ease, box-shadow 0.2s ease;
} }
.admin-login-container button:hover { .admin-login-container button:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 16px 28px rgba(188, 118, 0, 0.4);
} }
.admin-login-container .back-link { .admin-login-container .back-link {
@@ -1705,22 +1685,21 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
} }
.admin-login-container .back-link a { .admin-login-container .back-link a {
color: var(--sun-700); color: #667eea;
text-decoration: none; text-decoration: none;
font-weight: 600; font-weight: 600;
} }
.admin-login-container .back-link a:hover { .admin-login-container .back-link a:hover {
color: var(--sun-900);
text-decoration: underline; text-decoration: underline;
} }
.admin-dashboard { .admin-dashboard {
width: 95%; width: 95%;
max-width: 1400px; max-width: 1400px;
background: var(--sun-50); background: white;
border-radius: 20px; border-radius: 20px;
box-shadow: 0 20px 60px rgba(60, 42, 0, 0.25); box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 30px; padding: 30px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -1736,7 +1715,7 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
.admin-dashboard-header h1 { .admin-dashboard-header h1 {
font-size: 26px; font-size: 26px;
color: var(--sun-700); color: #4c51bf;
} }
.admin-dashboard-header button { .admin-dashboard-header button {
@@ -1762,12 +1741,12 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
.admin-stat-card { .admin-stat-card {
padding: 20px; padding: 20px;
border-radius: 16px; border-radius: 16px;
background: linear-gradient(135deg, var(--sun-500) 0%, var(--sun-700) 100%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; color: white;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
box-shadow: 0 12px 30px rgba(188, 118, 0, 0.35); box-shadow: 0 12px 30px rgba(102, 126, 234, 0.35);
} }
.admin-stat-card span { .admin-stat-card span {
@@ -1787,10 +1766,10 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
} }
.admin-section { .admin-section {
background: rgba(255,255,255,0.9); background: #f9fafb;
border-radius: 16px; border-radius: 16px;
padding: 20px; padding: 20px;
border: 1px solid rgba(188, 118, 0, 0.18); border: 1px solid #e5e7eb;
} }
.admin-section h2 { .admin-section h2 {
@@ -2175,11 +2154,11 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
height: 100%; height: 100%;
background: rgba(255,255,255,0.6); background: rgba(255,255,255,0.6);
} }
.chat-messages-header { .chat-messages-header {
background: rgba(255,255,255,0.94); background: white;
padding: 18px 24px; padding: 15px 20px;
border-bottom: 1px solid rgba(188, 118, 0, 0.18); border-bottom: 1px solid #e0e0e0;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
@@ -2834,108 +2813,51 @@ async function loadUsers() {
function renderUserList() { function renderUserList() {
const userList = document.getElementById('userList'); const userList = document.getElementById('userList');
if (!userList) { const searchTerm = document.getElementById('userSearch').value.toLowerCase();
return;
}
const searchInput = document.getElementById('userSearch'); const filtered = state.users.filter(u => u.display_name.toLowerCase().includes(searchTerm));
const searchTerm = (searchInput?.value || '').toLowerCase();
const filtered = state.users.filter(u => (u.display_name || '').toLowerCase().includes(searchTerm)); userList.innerHTML = filtered.map(user => `
<div class="user-item ${user.id === state.selectedUserId ? 'active' : ''}" onclick="selectUser(${user.id}, '${user.display_name}')">
if (filtered.length === 0) { <div class="user-avatar">
userList.innerHTML = '<div class="empty-user-list">Keine Nutzer gefunden.</div>'; ${user.username.charAt(0).toUpperCase()}
return; <div class="online-indicator ${user.is_online ? '' : 'offline-indicator'}"></div>
} </div>
<div class="user-info-text">
userList.innerHTML = filtered.map(user => { <div class="user-name">${user.display_name}</div>
const displayNameRaw = (user.display_name || user.username || '').trim(); <div class="user-status">${user.is_online ? 'Online' : 'Offline'}</div>
const displayName = displayNameRaw.length > 0 ? displayNameRaw : 'Unbekannt'; </div>
const initial = displayName.charAt(0).toUpperCase() || '?'; ${user.unread_count > 0 ? `<div class="unread-badge">${user.unread_count}</div>` : ''}
</div>
return ` `).join('');
<button type="button" class="user-item ${user.id === state.selectedUserId ? 'active' : ''}" data-user-id="${user.id}">
<div class="user-avatar">
${escapeHtml(initial)}
<div class="online-indicator ${user.is_online ? '' : 'offline-indicator'}"></div>
</div>
<div class="user-info-text">
<div class="user-name">${escapeHtml(displayName)}</div>
<div class="user-status">${user.is_online ? 'Online' : 'Offline'}</div>
</div>
${user.unread_count > 0 ? `<div class="unread-badge">${user.unread_count}</div>` : ''}
</button>
`;
}).join('');
userList.querySelectorAll('.user-item').forEach(item => {
item.addEventListener('click', () => {
const userId = Number(item.dataset.userId);
const user = state.users.find(u => u.id === userId);
if (!user) {
return;
}
selectUser(user.id, user.display_name || user.username);
});
});
} }
function selectUser(userId, displayName) { function selectUser(userId, displayName) {
state.selectedUserId = userId; state.selectedUserId = userId;
const welcome = document.getElementById('chatWelcome'); document.getElementById('chatWelcome').style.display = 'none';
if (welcome) { document.getElementById('chatMessagesContainer').style.display = 'flex';
welcome.style.display = 'none';
}
const container = document.getElementById('chatMessagesContainer'); document.getElementById('chatMessagesHeader').innerHTML = `
if (container) { <div class="user-avatar">${displayName.charAt(0).toUpperCase()}</div>
container.style.display = 'flex'; <div><div class="user-name">${displayName}</div></div>
} `;
const safeDisplayName = (displayName || '').trim() || 'Unbekannt';
const initial = safeDisplayName.charAt(0).toUpperCase() || '?';
const header = document.getElementById('chatMessagesHeader');
if (header) {
header.innerHTML = `
<div class="user-avatar">${escapeHtml(initial)}</div>
<div><div class="user-name">${escapeHtml(safeDisplayName)}</div></div>
`;
}
const messageList = document.getElementById('chatMessages');
if (messageList) {
messageList.innerHTML = '<div class="loading-state">Nachrichten werden geladen…</div>';
}
loadMessages(userId); loadMessages(userId);
renderUserList(); renderUserList();
} }
async function loadMessages(userId) { async function loadMessages(userId) {
const messageList = document.getElementById('chatMessages'); const response = await fetch(`?action=get_messages&user_id=${userId}`);
const result = await response.json();
try { if (result.success) {
const response = await fetch(`?action=get_messages&user_id=${userId}`); state.messages = result.messages;
const result = await response.json(); renderMessages();
markAsRead(userId);
if (result.success) { if (result.messages.length > 0) {
state.messages = result.messages; state.lastMessageId = Math.max(...result.messages.map(m => m.id));
renderMessages();
markAsRead(userId);
if (result.messages.length > 0) {
state.lastMessageId = Math.max(...result.messages.map(m => m.id));
}
} else if (messageList) {
state.messages = [];
messageList.innerHTML = `<div class="error-state">${escapeHtml(result.error || 'Nachrichten konnten nicht geladen werden.')}</div>`;
}
} catch (error) {
if (messageList) {
state.messages = [];
messageList.innerHTML = '<div class="error-state">Verbindung fehlgeschlagen. Bitte versuche es erneut.</div>';
} }
} }
} }
@@ -2943,15 +2865,6 @@ async function loadMessages(userId) {
function renderMessages() { function renderMessages() {
const container = document.getElementById('chatMessages'); const container = document.getElementById('chatMessages');
if (!container) {
return;
}
if (!state.messages || state.messages.length === 0) {
container.innerHTML = '<div class="empty-messages">Noch keine Nachrichten. Starte den Chat!</div>';
return;
}
container.innerHTML = state.messages.map(msg => { container.innerHTML = state.messages.map(msg => {
const isSent = msg.from_user_id === state.currentUserId; const isSent = msg.from_user_id === state.currentUserId;
const time = new Date(msg.timestamp).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); const time = new Date(msg.timestamp).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });