Merge branch 'main' into codex/lese-chat-programm-und-warte-auf-instruktionen-t4t8ra
This commit is contained in:
@@ -1470,22 +1470,9 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
|
|||||||
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,7 +1534,7 @@ 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;
|
||||||
@@ -1558,16 +1545,15 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
|
|||||||
.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 {
|
||||||
@@ -2177,9 +2156,9 @@ if (isset($_GET['stream']) && $_GET['stream'] === 'events') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.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,89 +2813,41 @@ 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) {
|
|
||||||
userList.innerHTML = '<div class="empty-user-list">Keine Nutzer gefunden.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
userList.innerHTML = filtered.map(user => {
|
|
||||||
const displayNameRaw = (user.display_name || user.username || '').trim();
|
|
||||||
const displayName = displayNameRaw.length > 0 ? displayNameRaw : 'Unbekannt';
|
|
||||||
const initial = displayName.charAt(0).toUpperCase() || '?';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<button type="button" class="user-item ${user.id === state.selectedUserId ? 'active' : ''}" data-user-id="${user.id}">
|
|
||||||
<div class="user-avatar">
|
<div class="user-avatar">
|
||||||
${escapeHtml(initial)}
|
${user.username.charAt(0).toUpperCase()}
|
||||||
<div class="online-indicator ${user.is_online ? '' : 'offline-indicator'}"></div>
|
<div class="online-indicator ${user.is_online ? '' : 'offline-indicator'}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-info-text">
|
<div class="user-info-text">
|
||||||
<div class="user-name">${escapeHtml(displayName)}</div>
|
<div class="user-name">${user.display_name}</div>
|
||||||
<div class="user-status">${user.is_online ? 'Online' : 'Offline'}</div>
|
<div class="user-status">${user.is_online ? 'Online' : 'Offline'}</div>
|
||||||
</div>
|
</div>
|
||||||
${user.unread_count > 0 ? `<div class="unread-badge">${user.unread_count}</div>` : ''}
|
${user.unread_count > 0 ? `<div class="unread-badge">${user.unread_count}</div>` : ''}
|
||||||
</button>
|
</div>
|
||||||
`;
|
`).join('');
|
||||||
}).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');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`?action=get_messages&user_id=${userId}`);
|
const response = await fetch(`?action=get_messages&user_id=${userId}`);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
@@ -2928,30 +2859,12 @@ async function loadMessages(userId) {
|
|||||||
if (result.messages.length > 0) {
|
if (result.messages.length > 0) {
|
||||||
state.lastMessageId = Math.max(...result.messages.map(m => m.id));
|
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>';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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' });
|
||||||
|
|||||||
Reference in New Issue
Block a user