340 lines
12 KiB
JavaScript
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;
|
|
}
|