Files
Projektarbeit-MYP/backend/static/js/admin-guest-requests.min.js
2025-06-13 07:32:57 +02:00

340 lines
12 KiB
JavaScript

/**
* Mercedes-Benz MYP Admin Guest Requests Management
* Moderne Verwaltung von Gastaufträgen mit Live-Updates
*/
// Vereinfachte minimierte Version mit korrigierten API-URLs
const API_BASE_URL = document.location.origin;
let csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
let currentRequests = [];
let filteredRequests = [];
document.addEventListener('DOMContentLoaded', function() {
console.log('🎯 Admin Guest Requests Manager geladen');
initEventListeners();
loadGuestRequests();
startAutoRefresh();
});
function initEventListeners() {
document.getElementById('search-requests')?.addEventListener('input', handleSearch);
document.getElementById('status-filter')?.addEventListener('change', handleFilterChange);
document.getElementById('sort-order')?.addEventListener('change', handleSortChange);
document.getElementById('refresh-btn')?.addEventListener('click', loadGuestRequests);
document.getElementById('export-btn')?.addEventListener('click', handleExport);
document.getElementById('bulk-actions-btn')?.addEventListener('click', showBulkActionsModal);
document.getElementById('select-all')?.addEventListener('change', handleSelectAll);
}
async function loadGuestRequests() {
try {
showLoading(true);
const url = `${API_BASE_URL}/api/admin/requests`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
currentRequests = data.requests || [];
updateStats(data.stats || {});
applyFiltersAndSort();
console.log(`${currentRequests.length} Gastaufträge geladen`);
} else {
throw new Error(data.message || 'Fehler beim Laden der Gastaufträge');
}
} catch (error) {
console.error('Fehler beim Laden der Gastaufträge:', error);
showNotification('❌ Fehler beim Laden der Gastaufträge: ' + error.message, 'error');
showEmptyState();
} finally {
showLoading(false);
}
}
async function approveRequest(requestId) {
const notes = prompt('Genehmigungsnotizen (optional):');
if (notes === null) return;
try {
showLoading(true);
const url = `${API_BASE_URL}/api/requests/${requestId}/approve`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify({ notes: notes || '' })
});
const data = await response.json();
if (data.success) {
showNotification('✅ Gastauftrag erfolgreich genehmigt', 'success');
if (data.otp) {
showNotification(`🔑 OTP-Code für Gast: ${data.otp}`, 'info');
}
loadGuestRequests();
} else {
throw new Error(data.message || 'Fehler beim Genehmigen');
}
} catch (error) {
console.error('Fehler beim Genehmigen:', error);
showNotification('❌ Fehler beim Genehmigen: ' + error.message, 'error');
} finally {
showLoading(false);
}
}
async function rejectRequest(requestId) {
const reason = prompt('Grund für die Ablehnung (erforderlich):');
if (!reason || reason.trim() === '') {
showNotification('⚠️ Ablehnungsgrund ist erforderlich', 'warning');
return;
}
try {
showLoading(true);
const url = `${API_BASE_URL}/api/requests/${requestId}/deny`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify({ reason: reason.trim() })
});
const data = await response.json();
if (data.success) {
showNotification('✅ Gastauftrag erfolgreich abgelehnt', 'success');
loadGuestRequests();
} else {
throw new Error(data.message || 'Fehler beim Ablehnen');
}
} catch (error) {
console.error('Fehler beim Ablehnen:', error);
showNotification('❌ Fehler beim Ablehnen: ' + error.message, 'error');
} finally {
showLoading(false);
}
}
async function deleteRequest(requestId) {
if (!confirm('Möchten Sie diesen Gastauftrag wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.')) return;
try {
showLoading(true);
const url = `${API_BASE_URL}/api/admin/requests/${requestId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
}
});
const data = await response.json();
if (data.success) {
showNotification('✅ Gastauftrag erfolgreich gelöscht', 'success');
loadGuestRequests();
} else {
throw new Error(data.message || 'Fehler beim Löschen');
}
} catch (error) {
console.error('Fehler beim Löschen:', error);
showNotification('❌ Fehler beim Löschen: ' + error.message, 'error');
} finally {
showLoading(false);
}
}
// Utility Functions
function updateStats(stats) {
document.getElementById('pending-count').textContent = stats.pending || 0;
document.getElementById('approved-count').textContent = stats.approved || 0;
document.getElementById('rejected-count').textContent = stats.denied || 0;
document.getElementById('total-count').textContent = stats.total || 0;
}
function applyFiltersAndSort() {
const searchTerm = document.getElementById('search-requests')?.value.toLowerCase() || '';
const statusFilter = document.getElementById('status-filter')?.value || 'all';
const sortOrder = document.getElementById('sort-order')?.value || 'newest';
let requests = [...currentRequests];
if (searchTerm) {
requests = requests.filter(req =>
(req.name || '').toLowerCase().includes(searchTerm) ||
(req.email || '').toLowerCase().includes(searchTerm) ||
(req.file_name || '').toLowerCase().includes(searchTerm)
);
}
if (statusFilter !== 'all') {
requests = requests.filter(req => req.status === statusFilter);
}
requests.sort((a, b) => {
if (sortOrder === 'oldest') {
return new Date(a.created_at) - new Date(b.created_at);
}
return new Date(b.created_at) - new Date(a.created_at);
});
filteredRequests = requests;
renderRequestsTable();
}
function renderRequestsTable() {
const tableBody = document.getElementById('requests-table-body');
if (!tableBody) return;
if (filteredRequests.length === 0) {
tableBody.innerHTML = '<tr><td colspan="7" class="text-center py-8 text-gray-500">Keine Gastaufträge gefunden</td></tr>';
return;
}
const requestsHtml = filteredRequests.map(request => createRequestRow(request)).join('');
tableBody.innerHTML = requestsHtml;
}
function createRequestRow(request) {
const statusColors = {
'pending': 'bg-yellow-100 text-yellow-800',
'approved': 'bg-green-100 text-green-800',
'rejected': 'bg-red-100 text-red-800'
};
return `
<tr class="hover:bg-gray-50" data-request-id="${request.id}">
<td class="px-6 py-4">
<input type="checkbox" class="request-checkbox" value="${request.id}">
</td>
<td class="px-6 py-4">
<div class="font-medium">${escapeHtml(request.name || 'Unbekannt')}</div>
<div class="text-gray-500">${escapeHtml(request.email || '')}</div>
</td>
<td class="px-6 py-4">
<div class="font-medium">${escapeHtml(request.file_name || 'Keine Datei')}</div>
<div class="text-gray-500">${request.duration_minutes || 0} Min.</div>
</td>
<td class="px-6 py-4">
<span class="px-2 py-1 text-xs rounded-full ${statusColors[request.status] || 'bg-gray-100 text-gray-800'}">
${getStatusText(request.status)}
</span>
</td>
<td class="px-6 py-4 text-gray-500">
${formatDateTime(request.created_at)}
</td>
<td class="px-6 py-4">Normal</td>
<td class="px-6 py-4">
<div class="flex space-x-2">
${request.status === 'pending' ? `
<button onclick="approveRequest(${request.id})" class="text-green-600 hover:text-green-900" title="Genehmigen">
</button>
<button onclick="rejectRequest(${request.id})" class="text-red-600 hover:text-red-900" title="Ablehnen">
</button>
` : ''}
<button onclick="deleteRequest(${request.id})" class="text-gray-600 hover:text-gray-900" title="Löschen">
🗑
</button>
</div>
</td>
</tr>
`;
}
// Event Handlers
function handleSearch() { applyFiltersAndSort(); }
function handleFilterChange() { applyFiltersAndSort(); }
function handleSortChange() { applyFiltersAndSort(); }
function handleSelectAll(event) {
document.querySelectorAll('.request-checkbox').forEach(cb => cb.checked = event.target.checked);
}
function handleExport() { showNotification('Export-Funktion wird implementiert', 'info'); }
// UI Functions
function showLoading(show) {
const overlay = document.getElementById('loading-overlay');
if (overlay) overlay.classList.toggle('hidden', !show);
}
function showEmptyState() {
document.getElementById('empty-state')?.classList.remove('hidden');
}
function showNotification(message, type = 'info') {
const colors = {
'success': 'bg-green-500',
'error': 'bg-red-500',
'warning': 'bg-yellow-500',
'info': 'bg-blue-500'
};
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-6 py-3 rounded-lg shadow-lg z-50`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 5000);
}
function startAutoRefresh() {
setInterval(loadGuestRequests, 30000); // Refresh every 30 seconds
}
function showBulkActionsModal() {
const selectedIds = Array.from(document.querySelectorAll('.request-checkbox:checked')).map(cb => cb.value);
if (selectedIds.length === 0) {
showNotification('⚠️ Bitte wählen Sie mindestens einen Gastauftrag aus', 'warning');
return;
}
document.getElementById('bulk-modal')?.classList.remove('hidden');
}
function closeBulkModal() {
document.getElementById('bulk-modal')?.classList.add('hidden');
}
// Utility Functions
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatDateTime(dateString) {
return new Date(dateString).toLocaleString('de-DE');
}
function getStatusText(status) {
const texts = {
'pending': 'Wartend',
'approved': 'Genehmigt',
'rejected': 'Abgelehnt'
};
return texts[status] || status;
}