223 lines
22 KiB
JavaScript
223 lines
22 KiB
JavaScript
let currentRequests=[];let filteredRequests=[];let currentPage=0;let totalPages=0;let totalRequests=0;let refreshInterval=null;let csrfToken='';function detectApiBaseUrl(){return'';}
|
||
const API_BASE_URL=detectApiBaseUrl();document.addEventListener('DOMContentLoaded',function(){csrfToken=document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')||'';initEventListeners();loadGuestRequests();startAutoRefresh();console.log('🎯 Admin Guest Requests Management geladen');});function initEventListeners(){const searchInput=document.getElementById('search-requests');if(searchInput){searchInput.addEventListener('input',debounce(handleSearch,300));}
|
||
const statusFilter=document.getElementById('status-filter');if(statusFilter){statusFilter.addEventListener('change',handleFilterChange);}
|
||
const sortOrder=document.getElementById('sort-order');if(sortOrder){sortOrder.addEventListener('change',handleSortChange);}
|
||
const refreshBtn=document.getElementById('refresh-btn');if(refreshBtn){refreshBtn.addEventListener('click',()=>{loadGuestRequests();showNotification('🔄 Gastaufträge aktualisiert','info');});}
|
||
const exportBtn=document.getElementById('export-btn');if(exportBtn){exportBtn.addEventListener('click',handleExport);}
|
||
const bulkActionsBtn=document.getElementById('bulk-actions-btn');if(bulkActionsBtn){bulkActionsBtn.addEventListener('click',showBulkActionsModal);}
|
||
const selectAllCheckbox=document.getElementById('select-all');if(selectAllCheckbox){selectAllCheckbox.addEventListener('change',handleSelectAll);}}
|
||
async function loadGuestRequests(){try{showLoading(true);const url=`${API_BASE_URL}/api/admin/guest-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||[];totalRequests=data.total||0;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);}}
|
||
function updateStats(stats){const elements={'pending-count':stats.pending||0,'approved-count':stats.approved||0,'rejected-count':stats.rejected||0,'total-count':stats.total||0};Object.entries(elements).forEach(([id,value])=>{const element=document.getElementById(id);if(element){animateCounter(element,value);}});}
|
||
function animateCounter(element,targetValue){const currentValue=parseInt(element.textContent)||0;const difference=targetValue-currentValue;const steps=20;const stepValue=difference/steps;let step=0;const interval=setInterval(()=>{step++;const value=Math.round(currentValue+(stepValue*step));element.textContent=value;if(step>=steps){clearInterval(interval);element.textContent=targetValue;}},50);}
|
||
function applyFiltersAndSort(){let requests=[...currentRequests];const statusFilter=document.getElementById('status-filter')?.value;if(statusFilter&&statusFilter!=='all'){requests=requests.filter(req=>req.status===statusFilter);}
|
||
const searchTerm=document.getElementById('search-requests')?.value.toLowerCase();if(searchTerm){requests=requests.filter(req=>req.name?.toLowerCase().includes(searchTerm)||req.email?.toLowerCase().includes(searchTerm)||req.file_name?.toLowerCase().includes(searchTerm)||req.reason?.toLowerCase().includes(searchTerm));}
|
||
const sortOrder=document.getElementById('sort-order')?.value;requests.sort((a,b)=>{switch(sortOrder){case'oldest':return new Date(a.created_at)-new Date(b.created_at);case'priority':return getPriorityValue(b)-getPriorityValue(a);case'newest':default:return new Date(b.created_at)-new Date(a.created_at);}});filteredRequests=requests;renderRequestsTable();}
|
||
function getPriorityValue(request){const now=new Date();const created=new Date(request.created_at);const hoursOld=(now-created)/(1000*60*60);let priority=0;if(request.status==='pending')priority+=10;else if(request.status==='approved')priority+=5;if(hoursOld>24)priority+=5;else if(hoursOld>8)priority+=3;else if(hoursOld>2)priority+=1;return priority;}
|
||
function renderRequestsTable(){const tableBody=document.getElementById('requests-table-body');const emptyState=document.getElementById('empty-state');if(!tableBody)return;if(filteredRequests.length===0){tableBody.innerHTML='';showEmptyState();return;}
|
||
hideEmptyState();const requestsHtml=filteredRequests.map(request=>createRequestRow(request)).join('');tableBody.innerHTML=requestsHtml;addRowEventListeners();}
|
||
function createRequestRow(request){const statusColor=getStatusColor(request.status);const priorityLevel=getPriorityLevel(request);const timeAgo=getTimeAgo(request.created_at);return`<tr class="hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors duration-200"data-request-id="${request.id}"><td class="px-6 py-4"><input type="checkbox"class="request-checkbox rounded border-slate-300 text-blue-600 focus:ring-blue-500"
|
||
value="${request.id}"></td><td class="px-6 py-4"><div class="flex items-center"><div class="flex-shrink-0 h-10 w-10"><div class="h-10 w-10 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center text-white font-semibold">${request.name?request.name[0].toUpperCase():'G'}</div></div><div class="ml-4"><div class="text-sm font-medium text-slate-900 dark:text-white">${escapeHtml(request.name||'Unbekannt')}</div><div class="text-sm text-slate-500 dark:text-slate-400">${escapeHtml(request.email||'Keine E-Mail')}</div></div></div></td><td class="px-6 py-4"><div class="text-sm text-slate-900 dark:text-white font-medium">${escapeHtml(request.file_name||'Keine Datei')}</div><div class="text-sm text-slate-500 dark:text-slate-400">${request.duration_minutes?`${request.duration_minutes}Min.`:'Unbekannte Dauer'}
|
||
${request.copies?`• ${request.copies}Kopien`:''}</div>${request.reason?`<div class="text-xs text-slate-400 dark:text-slate-500 mt-1 truncate max-w-xs">${escapeHtml(request.reason)}</div>`:''}</td><td class="px-6 py-4"><span class="inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full ${statusColor}"><span class="w-2 h-2 mr-1 rounded-full ${getStatusDot(request.status)}"></span>${getStatusText(request.status)}</span></td><td class="px-6 py-4"><div class="text-sm text-slate-900 dark:text-white">${timeAgo}</div><div class="text-xs text-slate-500 dark:text-slate-400">${formatDateTime(request.created_at)}</div></td><td class="px-6 py-4"><div class="flex items-center">${getPriorityBadge(priorityLevel)}
|
||
${request.is_urgent?'<span class="ml-2 text-red-500 text-xs">🔥 Dringend</span>':''}</div></td><td class="px-6 py-4"><div class="flex items-center space-x-2"><button onclick="showRequestDetail(${request.id})"
|
||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300 transition-colors"
|
||
title="Details anzeigen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>${request.status==='pending'?`<button onclick="approveRequest(${request.id})"
|
||
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300 transition-colors"
|
||
title="Genehmigen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></button><button onclick="rejectRequest(${request.id})"
|
||
class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 transition-colors"
|
||
title="Ablehnen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></button>`:''}<button onclick="deleteRequest(${request.id})"
|
||
class="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 transition-colors"
|
||
title="Löschen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg></button></div></td></tr>`;}
|
||
function getStatusColor(status){const colors={'pending':'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300','approved':'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300','rejected':'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300','expired':'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300'};return colors[status]||'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300';}
|
||
function getStatusDot(status){const dots={'pending':'bg-yellow-400 dark:bg-yellow-300','approved':'bg-green-400 dark:bg-green-300','rejected':'bg-red-400 dark:bg-red-300','expired':'bg-gray-400 dark:bg-gray-300'};return dots[status]||'bg-gray-400 dark:bg-gray-300';}
|
||
function getStatusText(status){const texts={'pending':'Wartend','approved':'Genehmigt','rejected':'Abgelehnt','expired':'Abgelaufen'};return texts[status]||status;}
|
||
function getPriorityLevel(request){const priority=getPriorityValue(request);if(priority>=15)return'high';if(priority>=8)return'medium';return'low';}
|
||
function getPriorityBadge(level){const badges={'high':'<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300">🔴 Hoch</span>','medium':'<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300">🟡 Mittel</span>','low':'<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300">🟢 Niedrig</span>'};return badges[level]||badges['low'];}
|
||
async function approveRequest(requestId){if(!confirm('Möchten Sie diesen Gastauftrag wirklich genehmigen?'))return;try{showLoading(true);const url=`${API_BASE_URL}/api/guest-requests/${requestId}/approve`;const response=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken},body:JSON.stringify({})});const data=await response.json();if(data.success){showNotification('✅ Gastauftrag erfolgreich genehmigt','success');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:');if(!reason)return;try{showLoading(true);const url=`${API_BASE_URL}/api/guest-requests/${requestId}/reject`;const response=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken},body:JSON.stringify({reason})});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/guest-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);}}
|
||
function showRequestDetail(requestId){const request=currentRequests.find(req=>req.id===requestId);if(!request)return;const modal=document.getElementById('detail-modal');const content=document.getElementById('modal-content');content.innerHTML=`<div class="p-6 border-b border-gray-200 dark:border-gray-700"><div class="flex justify-between items-center"><h3 class="text-xl font-bold text-gray-900 dark:text-white">Gastauftrag Details</h3><button onclick="closeDetailModal()"class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"><svg class="w-6 h-6"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg></button></div></div><div class="p-6"><div class="grid grid-cols-1 md:grid-cols-2 gap-6"><div class="space-y-4"><h4 class="text-lg font-semibold text-gray-900 dark:text-white">Antragsteller</h4><div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"><p><strong>Name:</strong>${escapeHtml(request.name||'Unbekannt')}</p><p><strong>E-Mail:</strong>${escapeHtml(request.email||'Keine E-Mail')}</p><p><strong>Erstellt am:</strong>${formatDateTime(request.created_at)}</p></div></div><div class="space-y-4"><h4 class="text-lg font-semibold text-gray-900 dark:text-white">Auftrag Details</h4><div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"><p><strong>Datei:</strong>${escapeHtml(request.file_name||'Keine Datei')}</p><p><strong>Dauer:</strong>${request.duration_minutes||'Unbekannt'}Minuten</p><p><strong>Kopien:</strong>${request.copies||1}</p><p><strong>Status:</strong>${getStatusText(request.status)}</p></div></div></div>${request.reason?`<div class="mt-6"><h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Begründung</h4><div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"><p class="text-gray-700 dark:text-gray-300">${escapeHtml(request.reason)}</p></div></div>`:''}<div class="mt-8 flex justify-end space-x-3">${request.status==='pending'?`<button onclick="approveRequest(${request.id}); closeDetailModal();"
|
||
class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">Genehmigen</button><button onclick="rejectRequest(${request.id}); closeDetailModal();"
|
||
class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">Ablehnen</button>`:''}<button onclick="closeDetailModal()"
|
||
class="px-4 py-2 bg-gray-300 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition-colors">Schließen</button></div></div>`;modal.classList.remove('hidden');}
|
||
function closeDetailModal(){const modal=document.getElementById('detail-modal');modal.classList.add('hidden');}
|
||
function showBulkActionsModal(){const selectedIds=getSelectedRequestIds();if(selectedIds.length===0){showNotification('⚠️ Bitte wählen Sie mindestens einen Gastauftrag aus','warning');return;}
|
||
const modal=document.getElementById('bulk-modal');modal.classList.remove('hidden');}
|
||
function closeBulkModal(){const modal=document.getElementById('bulk-modal');modal.classList.add('hidden');}
|
||
async function performBulkAction(action){const selectedIds=getSelectedRequestIds();if(selectedIds.length===0)return;const confirmMessages={'approve':`Möchten Sie ${selectedIds.length}Gastaufträge genehmigen?`,'reject':`Möchten Sie ${selectedIds.length}Gastaufträge ablehnen?`,'delete':`Möchten Sie ${selectedIds.length}Gastaufträge löschen?`};if(!confirm(confirmMessages[action]))return;try{showLoading(true);closeBulkModal();const promises=selectedIds.map(async(id)=>{const url=`${API_BASE_URL}/api/guest-requests/${id}/${action}`;const method=action==='delete'?'DELETE':'POST';return fetch(url,{method,headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken}});});const results=await Promise.allSettled(promises);const successCount=results.filter(r=>r.status==='fulfilled'&&r.value.ok).length;showNotification(`✅ ${successCount}von ${selectedIds.length}Aktionen erfolgreich`,'success');loadGuestRequests();document.getElementById('select-all').checked=false;document.querySelectorAll('.request-checkbox').forEach(cb=>cb.checked=false);}catch(error){console.error('Fehler bei Bulk-Aktion:',error);showNotification('❌ Fehler bei der Bulk-Aktion: '+error.message,'error');}finally{showLoading(false);}}
|
||
function getSelectedRequestIds(){const checkboxes=document.querySelectorAll('.request-checkbox:checked');return Array.from(checkboxes).map(cb=>parseInt(cb.value));}
|
||
function handleSearch(){applyFiltersAndSort();}
|
||
function handleFilterChange(){applyFiltersAndSort();}
|
||
function handleSortChange(){applyFiltersAndSort();}
|
||
function handleSelectAll(event){const checkboxes=document.querySelectorAll('.request-checkbox');checkboxes.forEach(checkbox=>{checkbox.checked=event.target.checked;});}
|
||
function handleExport(){const selectedIds=getSelectedRequestIds();const exportData=selectedIds.length>0?filteredRequests.filter(req=>selectedIds.includes(req.id)):filteredRequests;if(exportData.length===0){showNotification('⚠️ Keine Daten zum Exportieren verfügbar','warning');return;}
|
||
exportToCSV(exportData);}
|
||
function exportToCSV(data){const headers=['ID','Name','E-Mail','Datei','Status','Erstellt','Dauer (Min)','Kopien','Begründung'];const rows=data.map(req=>[req.id,req.name||'',req.email||'',req.file_name||'',getStatusText(req.status),formatDateTime(req.created_at),req.duration_minutes||'',req.copies||'',req.reason||'']);const csvContent=[headers,...rows].map(row=>row.map(field=>`"${String(field).replace(/"/g,'""')}"`).join(','))
|
||
.join('\n');
|
||
|
||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||
const link = document.createElement('a');
|
||
|
||
if (link.download !== undefined) {
|
||
const url = URL.createObjectURL(blob);
|
||
link.setAttribute('href', url);
|
||
link.setAttribute('download', `gastauftraege_${new Date().toISOString().split('T')[0]}.csv`);
|
||
link.style.visibility = 'hidden';
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
}
|
||
|
||
showNotification('📄 CSV-Export erfolgreich erstellt', 'success');
|
||
}
|
||
|
||
/**
|
||
* Auto-Refresh
|
||
*/
|
||
function startAutoRefresh() {
|
||
// Refresh alle 30 Sekunden
|
||
refreshInterval = setInterval(() => {
|
||
loadGuestRequests();
|
||
}, 30000);
|
||
}
|
||
|
||
function stopAutoRefresh() {
|
||
if (refreshInterval) {
|
||
clearInterval(refreshInterval);
|
||
refreshInterval = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Utility-Funktionen
|
||
*/
|
||
function addRowEventListeners() {
|
||
// Falls notwendig, können hier zusätzliche Event Listener hinzugefügt werden
|
||
}
|
||
|
||
function showLoading(show) {
|
||
const loadingElement = document.getElementById('table-loading');
|
||
const tableBody = document.getElementById('requests-table-body');
|
||
|
||
if (loadingElement) {
|
||
loadingElement.classList.toggle('hidden', !show);
|
||
}
|
||
|
||
if (show && tableBody) {
|
||
tableBody.innerHTML = '';
|
||
}
|
||
}
|
||
|
||
function showEmptyState() {
|
||
const emptyState = document.getElementById('empty-state');
|
||
if (emptyState) {
|
||
emptyState.classList.remove('hidden');
|
||
}
|
||
}
|
||
|
||
function hideEmptyState() {
|
||
const emptyState = document.getElementById('empty-state');
|
||
if (emptyState) {
|
||
emptyState.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
function showNotification(message, type = 'info') {
|
||
const notification = document.createElement('div');
|
||
notification.className = `fixed top-4 right-4 px-6 py-4 rounded-xl shadow-2xl z-50 transform transition-all duration-500 translate-x-full ${
|
||
type === 'success' ? 'bg-green-500 text-white' :
|
||
type === 'error' ? 'bg-red-500 text-white' :
|
||
type === 'warning' ? 'bg-yellow-500 text-black' :
|
||
'bg-blue-500 text-white'
|
||
}`;
|
||
|
||
notification.innerHTML = `
|
||
<div class="flex items-center space-x-3">
|
||
<span class="text-lg">
|
||
${type === 'success' ? '✅' :
|
||
type === 'error' ? '❌' :
|
||
type === 'warning' ? '⚠️' : 'ℹ️'}
|
||
</span>
|
||
<span class="font-medium">${message}</span>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
// Animation einblenden
|
||
setTimeout(() => {
|
||
notification.classList.remove('translate-x-full');
|
||
}, 100);
|
||
|
||
// Nach 5 Sekunden entfernen
|
||
setTimeout(() => {
|
||
notification.classList.add('translate-x-full');
|
||
setTimeout(() => notification.remove(), 5000);
|
||
}, 5000);
|
||
}
|
||
|
||
function debounce(func, wait) {
|
||
let timeout;
|
||
return function executedFunction(...args) {
|
||
const later = () => {
|
||
clearTimeout(timeout);
|
||
func(...args);
|
||
};
|
||
clearTimeout(timeout);
|
||
timeout = setTimeout(later, wait);
|
||
};
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
const map = {
|
||
'&': '&',
|
||
'<': '<',
|
||
'>': '>',
|
||
'"': '"',
|
||
"'": '''
|
||
};
|
||
return text ? String(text).replace(/[&<>"']/g, m => map[m]) : '';
|
||
}
|
||
|
||
function formatDateTime(dateString) {
|
||
if (!dateString) return 'Unbekannt';
|
||
|
||
const date = new Date(dateString);
|
||
return date.toLocaleString('de-DE', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
}
|
||
|
||
function getTimeAgo(dateString) {
|
||
if (!dateString) return 'Unbekannt';
|
||
|
||
const now = new Date();
|
||
const date = new Date(dateString);
|
||
const diffMs = now - date;
|
||
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||
const diffDays = Math.floor(diffHours / 24);
|
||
|
||
if (diffDays > 0) {
|
||
return `vor ${diffDays} Tag${diffDays === 1 ? '' : 'en'}`;
|
||
} else if (diffHours > 0) {
|
||
return `vor ${diffHours} Stunde${diffHours === 1 ? '' : 'n'}`;
|
||
} else {
|
||
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
||
return `vor ${Math.max(1, diffMinutes)} Minute${diffMinutes === 1 ? '' : 'n'}`;
|
||
}
|
||
}
|
||
|
||
// Globale Funktionen für onclick-Handler
|
||
window.showRequestDetail = showRequestDetail;
|
||
window.approveRequest = approveRequest;
|
||
window.rejectRequest = rejectRequest;
|
||
window.deleteRequest = deleteRequest;
|
||
window.closeDetailModal = closeDetailModal;
|
||
window.closeBulkModal = closeBulkModal;
|
||
window.performBulkAction = performBulkAction;
|
||
|
||
console.log('📋 Admin Guest Requests JavaScript vollständig geladen' |