let currentLicense = null; let isCardFlipped = false; let cameraStream = null; // Event Listener für Nachrichten von FiveM window.addEventListener('message', function(event) { const data = event.data; switch(data.action) { case 'showLicense': showLicense(data.data); break; case 'hideLicense': closeLicense(); break; case 'openCamera': openCamera(); break; } }); // Lizenz anzeigen function showLicense(data) { currentLicense = data; const container = document.getElementById('license-container'); const card = document.getElementById('license-card'); // Loading anzeigen showLoading(); setTimeout(() => { // Lizenztyp-spezifische Klasse hinzufügen card.className = 'license-card ' + data.license.license_type; // Header befüllen document.getElementById('license-title').textContent = data.config.label; document.getElementById('license-icon').className = 'license-icon ' + data.config.icon; // Persönliche Daten document.getElementById('license-name').textContent = data.license.name || 'N/A'; document.getElementById('license-birthday').textContent = formatDate(data.license.birthday) || 'N/A'; document.getElementById('license-gender').textContent = formatGender(data.license.gender) || 'N/A'; // Dokument-Informationen document.getElementById('license-issue').textContent = formatDate(data.license.issue_date) || 'N/A'; document.getElementById('license-expire').textContent = formatDate(data.license.expire_date) || 'N/A'; document.getElementById('license-id').textContent = '#' + (data.license.id || '000000').toString().padStart(6, '0'); document.getElementById('license-issuer').textContent = data.license.issued_by_name || 'Behörde'; // Foto anzeigen displayPlayerPhoto(data.license); // Klassen anzeigen (nur bei Führerschein) displayLicenseClasses(data.license); // Status und Gültigkeit displayLicenseStatus(data.license); displayValidityIndicator(data.license); // Rückseite vorbereiten prepareBackSide(data.license); // Container anzeigen hideLoading(); container.classList.remove('hidden'); // Sound-Effekt playSound('card-flip-sound'); // Notification showNotification('Lizenz geladen', 'success'); }, 500); } // Spieler-Foto anzeigen function displayPlayerPhoto(license) { const photoImg = document.getElementById('player-photo'); const photoPlaceholder = document.getElementById('photo-placeholder'); if (license.photo_url && license.photo_url !== '') { photoImg.src = license.photo_url; photoImg.onload = function() { photoImg.classList.remove('hidden'); photoPlaceholder.classList.add('hidden'); }; photoImg.onerror = function() { photoImg.classList.add('hidden'); photoPlaceholder.classList.remove('hidden'); }; } else { photoImg.classList.add('hidden'); photoPlaceholder.classList.remove('hidden'); } } // Lizenz-Klassen anzeigen function displayLicenseClasses(license) { const classesRow = document.getElementById('license-classes-row'); const classesElement = document.getElementById('license-classes'); if (license.license_type === 'drivers_license' && license.classes && license.classes !== '[]') { try { const classes = JSON.parse(license.classes); if (classes && classes.length > 0) { classesElement.textContent = classes.join(', '); classesRow.style.display = 'flex'; return; } } catch (e) { console.error('Fehler beim Parsen der Klassen:', e); } } classesRow.style.display = 'none'; } // Lizenz-Status anzeigen function displayLicenseStatus(license) { const statusElement = document.getElementById('license-status'); const statusIcon = statusElement.querySelector('.status-icon'); const statusText = statusElement.querySelector('.status-text'); // Ablaufdatum prüfen let isExpired = false; let isExpiringSoon = false; if (license.expire_date) { const expireDate = new Date(license.expire_date); const today = new Date(); const daysUntilExpire = Math.ceil((expireDate - today) / (1000 * 60 * 60 * 24)); isExpired = daysUntilExpire < 0; isExpiringSoon = daysUntilExpire <= 30 && daysUntilExpire >= 0; } // Status setzen if (!license.is_active || isExpired) { statusElement.className = 'license-status inactive'; statusIcon.className = 'status-icon fas fa-times-circle'; statusText.textContent = isExpired ? 'Abgelaufen' : 'Ungültig'; } else if (isExpiringSoon) { statusElement.className = 'license-status warning'; statusIcon.className = 'status-icon fas fa-exclamation-triangle'; statusText.textContent = 'Läuft bald ab'; } else { statusElement.className = 'license-status active'; statusIcon.className = 'status-icon fas fa-check-circle'; statusText.textContent = 'Gültig'; } } // Gültigkeits-Indikator anzeigen function displayValidityIndicator(license) { const validityFill = document.getElementById('validity-fill'); const validityText = document.getElementById('validity-text'); if (!license.expire_date) { validityText.textContent = 'Unbegrenzt gültig'; validityFill.style.width = '100%'; validityFill.style.backgroundColor = '#4CAF50'; return; } const issueDate = new Date(license.issue_date); const expireDate = new Date(license.expire_date); const today = new Date(); const totalDays = Math.ceil((expireDate - issueDate) / (1000 * 60 * 60 * 24)); const remainingDays = Math.ceil((expireDate - today) / (1000 * 60 * 60 * 24)); const percentage = Math.max(0, Math.min(100, (remainingDays / totalDays) * 100)); validityFill.style.width = percentage + '%'; if (remainingDays < 0) { validityText.textContent = 'Abgelaufen'; validityFill.style.backgroundColor = '#f44336'; } else if (remainingDays <= 30) { validityText.textContent = `Noch ${remainingDays} Tage gültig`; validityFill.style.backgroundColor = '#ff9800'; } else { validityText.textContent = `Noch ${remainingDays} Tage gültig`; validityFill.style.backgroundColor = '#4CAF50'; } } // Rückseite vorbereiten function prepareBackSide(license) { const classesGrid = document.getElementById('classes-grid'); const restrictionsList = document.getElementById('restrictions-list'); const notesText = document.getElementById('notes-text'); // Klassen-Grid für Führerschein if (license.license_type === 'drivers_license' && license.classes) { try { const classes = JSON.parse(license.classes); classesGrid.innerHTML = ''; const classDescriptions = { 'A': 'Motorräder', 'A1': 'Leichte Motorräder', 'A2': 'Mittlere Motorräder', 'B': 'PKW', 'BE': 'PKW mit Anhänger', 'C': 'LKW', 'CE': 'LKW mit Anhänger', 'D': 'Bus', 'DE': 'Bus mit Anhänger' }; classes.forEach(cls => { const classItem = document.createElement('div'); classItem.className = 'class-item'; classItem.innerHTML = `
${cls}
${classDescriptions[cls] || 'Unbekannt'}
`; classesGrid.appendChild(classItem); }); } catch (e) { classesGrid.innerHTML = '

Keine Klassen verfügbar

'; } } else { classesGrid.innerHTML = '

Nicht zutreffend

'; } // Beschränkungen (Beispiel) restrictionsList.innerHTML = '
  • Keine besonderen Beschränkungen
  • '; // Bemerkungen notesText.textContent = license.notes || 'Keine besonderen Bemerkungen'; } // Karte drehen function flipCard() { const frontSide = document.querySelector('.license-content, .license-footer'); const backSide = document.getElementById('license-back'); isCardFlipped = !isCardFlipped; if (isCardFlipped) { // Zur Rückseite document.querySelector('.license-content').classList.add('hidden'); document.querySelector('.license-footer').classList.add('hidden'); backSide.classList.remove('hidden'); } else { // Zur Vorderseite document.querySelector('.license-content').classList.remove('hidden'); document.querySelector('.license-footer').classList.remove('hidden'); backSide.classList.add('hidden'); } playSound('card-flip-sound'); } // Kamera öffnen async function openCamera() { const container = document.getElementById('camera-container'); const video = document.getElementById('camera-video'); try { cameraStream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480, facingMode: 'user' } }); video.srcObject = cameraStream; container.classList.remove('hidden'); showNotification('Kamera geöffnet', 'info'); } catch (err) { console.error('Kamera-Zugriff fehlgeschlagen:', err); showNotification('Kamera-Zugriff fehlgeschlagen', 'error'); } } // Foto aufnehmen function takePhoto() { const video = document.getElementById('camera-video'); const canvas = document.getElementById('camera-canvas'); const ctx = canvas.getContext('2d'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0); const photoData = canvas.toDataURL('image/jpeg', 0.8); // Sound-Effekt playSound('camera-shutter-sound'); // An FiveM senden fetch(`https://${GetParentResourceName()}/savePhoto`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ photo: photoData, citizenid: currentLicense?.license?.citizenid }) }).then(() => { showNotification('Foto gespeichert', 'success'); closeCamera(); }).catch(err => { console.error('Fehler beim Speichern:', err); showNotification('Fehler beim Speichern', 'error'); }); } // Kamera schließen function closeCamera() { const container = document.getElementById('camera-container'); if (cameraStream) { cameraStream.getTracks().forEach(track => track.stop()); cameraStream = null; } container.classList.add('hidden'); } // Lizenz schließen function closeLicense() { const container = document.getElementById('license-container'); container.classList.add('hidden'); // Karte zurücksetzen isCardFlipped = false; document.querySelector('.license-content').classList.remove('hidden'); document.querySelector('.license-footer').classList.remove('hidden'); document.getElementById('license-back').classList.add('hidden'); // Callback an FiveM fetch(`https://${GetParentResourceName()}/closeLicense`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({}) }).catch(() => {}); // Fehler ignorieren } // Hilfsfunktionen function formatGender(gender) { const genderMap = { 'male': 'Männlich', 'female': 'Weiblich', 'other': 'Divers', 'm': 'Männlich', 'f': 'Weiblich' }; return genderMap[gender?.toLowerCase()] || gender || 'Unbekannt'; } function formatDate(dateString) { if (!dateString) return null; try { const date = new Date(dateString); return date.toLocaleDateString('de-DE'); } catch (e) { return dateString; } } function showLoading() { document.getElementById('loading-overlay').classList.remove('hidden'); } function hideLoading() { document.getElementById('loading-overlay').classList.add('hidden'); } function playSound(soundId) { const audio = document.getElementById(soundId); if (audio) { audio.volume = 0.3; audio.play().catch(() => {}); // Fehler ignorieren } } function showNotification(message, type = 'info') { const container = document.getElementById('notification-container'); const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.innerHTML = `
    ${message}
    `; container.appendChild(notification); // Auto-remove nach 5 Sekunden setTimeout(() => { if (notification.parentElement) { notification.remove(); } }, 5000); } function getNotificationIcon(type) { const icons = { 'success': 'fa-check-circle', 'error': 'fa-exclamation-circle', 'warning': 'fa-exclamation-triangle', 'info': 'fa-info-circle' }; return icons[type] || icons.info; } // Event Listeners document.addEventListener('keydown', function(event) { if (event.key === 'Escape') { if (!document.getElementById('camera-container').classList.contains('hidden')) { closeCamera(); } else if (!document.getElementById('license-container').classList.contains('hidden')) { closeLicense(); } } if (event.key === 'f' || event.key === 'F') { if (!document.getElementById('license-container').classList.contains('hidden')) { flipCard(); } } }); // Klick außerhalb zum Schließen document.getElementById('license-container').addEventListener('click', function(event) { if (event.target.classList.contains('license-overlay')) { closeLicense(); } }); // Initialisierung document.addEventListener('DOMContentLoaded', function() { console.log('License System UI geladen'); });