// Globale Variablen let currentData = null; let currentTargetId = null; let currentLicenseType = null; let formData = {}; // Utility-Funktionen function showLoading() { document.getElementById('loadingOverlay').style.display = 'flex'; } function hideLoading() { document.getElementById('loadingOverlay').style.display = 'none'; } function showError(message) { // Hier könnte eine Toast-Notification implementiert werden console.error(message); alert(message); // Temporär } function showSuccess(message) { // Hier könnte eine Toast-Notification implementiert werden console.log(message); } // Validierungsfunktionen function validateDate(dateString) { const regex = /^\d{2}\.\d{2}\.\d{4}$/; if (!regex.test(dateString)) return false; const [day, month, year] = dateString.split('.').map(Number); const date = new Date(year, month - 1, day); return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day && year >= 1900 && year <= 2100; } function validateUrl(url) { if (!url || url.trim() === '') return true; // Optional try { new URL(url); return url.startsWith('http://') || url.startsWith('https://'); } catch { return false; } } function validateEmail(email) { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(email); } // NUI Message Handler window.addEventListener('message', function(event) { const data = event.data; switch(data.action) { case 'openMainMenu': openMainMenu(data.data); break; case 'openCustomLicenseForm': openCustomLicenseForm(data.data); break; case 'showLicense': showLicense(data.data); break; case 'showMyLicense': showMyLicense(data.data); break; case 'showPlayerLicenses': showPlayerLicenses(data.data); break; } }); // Hauptmenü öffnen function openMainMenu(data) { currentData = data; currentTargetId = data.targetId; // Target-Sektion anzeigen/verstecken const targetSection = document.getElementById('targetSection'); const targetDistance = document.getElementById('targetDistance'); if (data.hasTarget) { targetSection.style.display = 'block'; targetDistance.textContent = data.targetDistance.toFixed(1); // Lizenz-Buttons generieren generateLicenseButtons(data.licenseTypes); } else { targetSection.style.display = 'none'; } document.getElementById('mainMenu').style.display = 'flex'; } // Lizenz-Buttons generieren function generateLicenseButtons(licenseTypes) { const container = document.getElementById('licenseButtons'); container.innerHTML = ''; Object.keys(licenseTypes).forEach(licenseType => { const config = licenseTypes[licenseType]; const button = document.createElement('button'); button.className = 'menu-btn'; button.style.background = `linear-gradient(135deg, ${config.color} 0%, ${adjustColor(config.color, -20)} 100%)`; button.onclick = () => openLicenseCreation(licenseType); button.innerHTML = ` ${config.label} `; container.appendChild(button); }); } // Farbe anpassen (für Gradient) function adjustColor(color, amount) { const usePound = color[0] === '#'; const col = usePound ? color.slice(1) : color; const num = parseInt(col, 16); let r = (num >> 16) + amount; let g = (num >> 8 & 0x00FF) + amount; let b = (num & 0x0000FF) + amount; r = r > 255 ? 255 : r < 0 ? 0 : r; g = g > 255 ? 255 : g < 0 ? 0 : g; b = b > 255 ? 255 : b < 0 ? 0 : b; return (usePound ? '#' : '') + (r << 16 | g << 8 | b).toString(16).padStart(6, '0'); } // Erweiterte Lizenz-Erstellung öffnen function openCustomLicenseForm(data) { currentData = data; currentTargetId = data.targetId; currentLicenseType = data.licenseType; formData = {}; const config = data.config; // Titel setzen document.getElementById('formTitle').textContent = `${config.label} erstellen`; // Inhaber-Name setzen (würde normalerweise vom Server kommen) document.getElementById('holderName').value = 'Spieler Name'; // Placeholder // Benutzerdefinierte Felder generieren generateCustomFields(config.custom_fields || []); // Klassen-Sektion (falls vorhanden) if (config.classes) { generateClassesSection(config.classes); document.getElementById('classesSection').style.display = 'block'; } else { document.getElementById('classesSection').style.display = 'none'; } // Vorschau initialisieren updatePreview(); // Form-Event-Listener setupFormEventListeners(); document.getElementById('customLicenseForm').style.display = 'flex'; } // Benutzerdefinierte Felder generieren function generateCustomFields(fields) { const container = document.getElementById('customFields'); container.innerHTML = ''; fields.forEach(field => { const fieldDiv = document.createElement('div'); fieldDiv.className = 'form-group'; const label = document.createElement('label'); label.textContent = field.label + (field.required ? ' *' : ''); label.setAttribute('for', field.name); let input; switch(field.type) { case 'text': case 'date': case 'number': case 'url': case 'email': input = document.createElement('input'); input.type = field.type === 'date' ? 'text' : field.type; input.placeholder = field.placeholder || ''; break; case 'textarea': input = document.createElement('textarea'); input.placeholder = field.placeholder || ''; input.rows = 3; break; case 'select': input = document.createElement('select'); const defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = 'Bitte wählen...'; input.appendChild(defaultOption); field.options.forEach(option => { const optionElement = document.createElement('option'); optionElement.value = option.value; optionElement.textContent = option.label; input.appendChild(optionElement); }); break; } input.id = field.name; input.name = field.name; input.required = field.required || false; // Event-Listener für Live-Validierung input.addEventListener('input', () => { validateField(field, input); updatePreview(); }); input.addEventListener('blur', () => { validateField(field, input); }); fieldDiv.appendChild(label); fieldDiv.appendChild(input); // Spezielle Behandlung für Bild-URL if (field.type === 'url' && field.name.includes('photo') || field.name.includes('logo')) { const imagePreview = document.createElement('div'); imagePreview.className = 'image-preview'; imagePreview.id = field.name + '_preview'; const placeholder = document.createElement('div'); placeholder.className = 'image-placeholder'; placeholder.innerHTML = '
Vorschau'; imagePreview.appendChild(placeholder); fieldDiv.appendChild(imagePreview); // Bild-Validierung input.addEventListener('input', () => { validateImageUrl(input.value, field.name); }); } container.appendChild(fieldDiv); }); } // Klassen-Sektion generieren function generateClassesSection(classes) { const container = document.getElementById('classesContainer'); container.innerHTML = ''; const checkboxGroup = document.createElement('div'); checkboxGroup.className = 'checkbox-group'; Object.keys(classes).forEach(classKey => { const checkboxItem = document.createElement('div'); checkboxItem.className = 'checkbox-item'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = 'class_' + classKey; checkbox.name = 'classes'; checkbox.value = classKey; const label = document.createElement('label'); label.setAttribute('for', 'class_' + classKey); label.textContent = `${classKey} - ${classes[classKey]}`; checkbox.addEventListener('change', updatePreview); checkboxItem.appendChild(checkbox); checkboxItem.appendChild(label); checkboxGroup.appendChild(checkboxItem); }); container.appendChild(checkboxGroup); } // Feld-Validierung function validateField(field, input) { const value = input.value.trim(); const formGroup = input.closest('.form-group'); // Vorherige Fehlermeldungen entfernen const existingError = formGroup.querySelector('.error-message'); if (existingError) { existingError.remove(); } formGroup.classList.remove('error'); // Pflichtfeld-Prüfung if (field.required && !value) { showFieldError(formGroup, 'Dieses Feld ist erforderlich'); return false; } if (!value) return true; // Optional und leer // Typ-spezifische Validierung let isValid = true; let errorMessage = ''; switch(field.type) { case 'date': isValid = validateDate(value); errorMessage = 'Ungültiges Datum (Format: TT.MM.JJJJ)'; break; case 'url': isValid = validateUrl(value); errorMessage = 'Ungültige URL (muss mit http:// oder https:// beginnen)'; break; case 'email': isValid = validateEmail(value); errorMessage = 'Ungültige E-Mail-Adresse'; break; case 'number': isValid = !isNaN(value) && value > 0; errorMessage = 'Muss eine positive Zahl sein'; break; } if (!isValid) { showFieldError(formGroup, errorMessage); return false; } // Erfolg anzeigen showFieldSuccess(formGroup); return true; } // Feld-Fehler anzeigen function showFieldError(formGroup, message) { formGroup.classList.add('error'); const errorDiv = document.createElement('div'); errorDiv.className = 'error-message'; errorDiv.innerHTML = ` ${message}`; formGroup.appendChild(errorDiv); } // Feld-Erfolg anzeigen function showFieldSuccess(formGroup) { const successDiv = document.createElement('div'); successDiv.className = 'success-message'; successDiv.innerHTML = ` Gültig`; formGroup.appendChild(successDiv); // Nach 2 Sekunden entfernen setTimeout(() => { if (successDiv.parentNode) { successDiv.remove(); } }, 2000); } // Bild-URL validieren function validateImageUrl(url, fieldName) { const previewContainer = document.getElementById(fieldName + '_preview'); if (!previewContainer) return; if (!url || url.trim() === '') { previewContainer.innerHTML = '

Vorschau
'; return; } // Loading anzeigen previewContainer.innerHTML = '

Lade...
'; // Bild laden testen const img = new Image(); img.onload = function() { previewContainer.innerHTML = `Vorschau`; updatePreview(); }; img.onerror = function() { previewContainer.innerHTML = '

Fehler beim Laden
'; }; img.src = url; } // Vorschau aktualisieren function updatePreview() { const config = currentData.config; // Name const holderName = document.getElementById('holderName').value || 'Unbekannt'; document.getElementById('previewName').textContent = holderName; // Typ document.getElementById('previewType').textContent = config.label; // Foto const photoField = config.custom_fields.find(f => f.name.includes('photo') || f.name.includes('logo')); if (photoField) { const photoUrl = document.getElementById(photoField.name).value || currentData.ui.default_avatar; document.getElementById('previewPhoto').src = photoUrl; } // Details const detailsContainer = document.getElementById('previewDetails'); detailsContainer.innerHTML = ''; config.custom_fields.forEach(field => { if (field.name.includes('photo') || field.name.includes('logo')) return; // Skip Bilder const value = document.getElementById(field.name).value; if (value) { const fieldDiv = document.createElement('div'); fieldDiv.className = 'preview-field'; fieldDiv.innerHTML = ` ${field.label}: ${value} `; detailsContainer.appendChild(fieldDiv); } }); // Klassen if (config.classes) { const selectedClasses = Array.from(document.querySelectorAll('input[name="classes"]:checked')) .map(cb => cb.value); if (selectedClasses.length > 0) { const classesDiv = document.createElement('div'); classesDiv.className = 'preview-field'; classesDiv.innerHTML = ` Klassen: ${selectedClasses.join(', ')} `; detailsContainer.appendChild(classesDiv); } } } // Form Event-Listener einrichten function setupFormEventListeners() { const form = document.getElementById('licenseForm'); form.addEventListener('submit', function(e) { e.preventDefault(); submitCustomLicense(); }); } // Benutzerdefinierte Lizenz einreichen function submitCustomLicense() { const config = currentData.config; const customData = {}; const classes = []; // Validierung aller Felder let isValid = true; config.custom_fields.forEach(field => { const input = document.getElementById(field.name); if (!validateField(field, input)) { isValid = false; } customData[field.name] = input.value.trim(); }); if (!isValid) { showError('Bitte korrigiere die markierten Fehler'); return; } // Klassen sammeln if (config.classes) { const selectedClasses = Array.from(document.querySelectorAll('input[name="classes"]:checked')); selectedClasses.forEach(cb => { classes.push({ key: cb.value, label: config.classes[cb.value] }); }); } showLoading(); // An Client senden fetch(`https://${GetParentResourceName()}/submitCustomLicense`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ licenseType: currentLicenseType, targetId: currentTargetId, customData: customData, classes: classes }) }) .then(response => response.json()) .then(data => { hideLoading(); if (data.success) { showSuccess('Lizenz erfolgreich erstellt!'); closeCustomForm(); } else { showError(data.error || 'Fehler beim Erstellen der Lizenz'); } }) .catch(error => { hideLoading(); showError('Netzwerkfehler: ' + error.message); }); } // Lizenz anzeigen function showLicense(data) { const license = data.license; const config = currentData?.licenseTypes?.[license.license_type] || {}; document.getElementById('licenseTitle').textContent = config.label || license.license_type; const card = document.getElementById('licenseCard'); card.innerHTML = generateLicenseCardHTML(license, config); document.getElementById('licenseDisplay').style.display = 'flex'; } // Eigene Lizenz anzeigen function showMyLicense(data) { showLicense(data); // Gleiche Funktion } // Spieler-Lizenzen anzeigen function showPlayerLicenses(data) { document.getElementById('playerName').textContent = data.targetName; const grid = document.getElementById('licensesGrid'); const noLicenses = document.getElementById('noLicenses'); if (data.licenses.length === 0) { grid.style.display = 'none'; noLicenses.style.display = 'block'; } else { grid.style.display = 'grid'; noLicenses.style.display = 'none'; grid.innerHTML = ''; data.licenses.forEach(license => { const config = data.licenseTypes[license.license_type] || {}; const item = document.createElement('div'); item.className = 'license-item'; item.innerHTML = `

${config.label || license.license_type}

Ausgestellt: ${formatDate(license.issue_date)}

${license.expire_date ? `

Gültig bis: ${formatDate(license.expire_date)}

` : '

Unbegrenzt gültig

'}
`; grid.appendChild(item); }); } document.getElementById('playerLicensesDisplay').style.display = 'flex'; } // Lizenz-Karte HTML generieren function generateLicenseCardHTML(license, config) { const customData = license.custom_data ? JSON.parse(license.custom_data) : {}; const classes = license.classes ? JSON.parse(license.classes) : []; let html = `

${config.label || license.license_type}

${config.description || 'Offizielle Lizenz'}

`; // Foto const photoUrl = customData.photo_url || customData.logo_url || currentData?.ui?.default_avatar || 'https://via.placeholder.com/120x150'; html += `
Foto
`; // Details html += `
`; // Basis-Informationen html += `
Name: ${license.holder_name || 'Unbekannt'}
Lizenz-Nr.: ${license.id}
Ausgestellt: ${formatDate(license.issue_date)}
`; if (license.expire_date) { html += `
Gültig bis: ${formatDate(license.expire_date)}
`; } html += `
Ausgestellt von: ${license.issued_by || 'System'}
`; // Benutzerdefinierte Felder Object.keys(customData).forEach(key => { if (key.includes('photo') || key.includes('logo')) return; // Skip Bilder const value = customData[key]; if (value) { const label = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); html += `
${label}: ${value}
`; } }); html += `
`; // Klassen if (classes.length > 0) { html += `

Berechtigte Klassen

`; classes.forEach(cls => { html += `
${cls.key}
`; }); html += `
`; } return html; } // Datum formatieren function formatDate(dateString) { if (!dateString) return 'Unbekannt'; const date = new Date(dateString); return date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }); } // Event-Handler für Buttons function closeMenu() { document.getElementById('mainMenu').style.display = 'none'; fetch(`https://${GetParentResourceName()}/closeMenu`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); } function closeCustomForm() { document.getElementById('customLicenseForm').style.display = 'none'; } function closeLicenseDisplay() { document.getElementById('licenseDisplay').style.display = 'none'; } function closePlayerLicenses() { document.getElementById('playerLicensesDisplay').style.display = 'none'; } function requestLicense() { fetch(`https://${GetParentResourceName()}/requestLicense`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ targetId: currentTargetId }) }); } function requestMyLicense(licenseType) { fetch(`https://${GetParentResourceName()}/requestMyLicense`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ licenseType: licenseType }) }); } function requestPlayerLicenses() { fetch(`https://${GetParentResourceName()}/requestPlayerLicenses`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ targetId: currentTargetId }) }); } function openLicenseCreation(licenseType) { fetch(`https://${GetParentResourceName()}/openLicenseCreation`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ licenseType: licenseType, targetId: currentTargetId }) }); } function viewLicenseDetails(licenseType) { // Lizenz-Details anzeigen (implementiere nach Bedarf) console.log('View license details:', licenseType); } function revokeLicense(licenseType) { if (confirm(`Möchtest du die ${licenseType} wirklich entziehen?`)) { fetch(`https://${GetParentResourceName()}/revokeLicense`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ licenseType: licenseType, targetId: currentTargetId }) }); } } // ESC-Taste zum Schließen document.addEventListener('keydown', function(event) { if (event.key === 'Escape') { // Schließe das oberste geöffnete Menü if (document.getElementById('customLicenseForm').style.display === 'flex') { closeCustomForm(); } else if (document.getElementById('licenseDisplay').style.display === 'flex') { closeLicenseDisplay(); } else if (document.getElementById('playerLicensesDisplay').style.display === 'flex') { closePlayerLicenses(); } else if (document.getElementById('mainMenu').style.display === 'flex') { closeMenu(); } } }); // Utility-Funktion für Resource-Name function GetParentResourceName() { return 'nordi_license'; // Ersetze mit deinem Resource-Namen } console.log('License System UI geladen (Erweiterte Version)');