2025-08-04 09:24:53 +02:00
|
|
|
// Globale Variablen
|
|
|
|
let currentData = null;
|
|
|
|
let currentTargetId = null;
|
|
|
|
let currentLicenseType = null;
|
|
|
|
let formData = {};
|
2025-08-04 06:14:47 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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
|
2025-08-04 06:14:47 +02:00
|
|
|
window.addEventListener('message', function(event) {
|
|
|
|
const data = event.data;
|
|
|
|
|
|
|
|
switch(data.action) {
|
2025-08-04 09:24:53 +02:00
|
|
|
case 'openMainMenu':
|
|
|
|
openMainMenu(data.data);
|
|
|
|
break;
|
|
|
|
case 'openCustomLicenseForm':
|
|
|
|
openCustomLicenseForm(data.data);
|
|
|
|
break;
|
2025-08-04 06:14:47 +02:00
|
|
|
case 'showLicense':
|
|
|
|
showLicense(data.data);
|
|
|
|
break;
|
2025-08-04 09:24:53 +02:00
|
|
|
case 'showMyLicense':
|
|
|
|
showMyLicense(data.data);
|
2025-08-04 06:53:42 +02:00
|
|
|
break;
|
2025-08-04 09:24:53 +02:00
|
|
|
case 'showPlayerLicenses':
|
|
|
|
showPlayerLicenses(data.data);
|
2025-08-04 06:53:42 +02:00
|
|
|
break;
|
2025-08-04 06:14:47 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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';
|
|
|
|
}
|
2025-08-04 06:14:47 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
document.getElementById('mainMenu').style.display = 'flex';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lizenz-Buttons generieren
|
|
|
|
function generateLicenseButtons(licenseTypes) {
|
|
|
|
const container = document.getElementById('licenseButtons');
|
|
|
|
container.innerHTML = '';
|
2025-08-04 06:14:47 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
Object.keys(licenseTypes).forEach(licenseType => {
|
|
|
|
const config = licenseTypes[licenseType];
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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);
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
button.innerHTML = `
|
|
|
|
<i class="${config.icon}"></i>
|
|
|
|
<span>${config.label}</span>
|
|
|
|
`;
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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';
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
const label = document.createElement('label');
|
|
|
|
label.textContent = field.label + (field.required ? ' *' : '');
|
|
|
|
label.setAttribute('for', field.name);
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
let input;
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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;
|
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
input.id = field.name;
|
|
|
|
input.name = field.name;
|
|
|
|
input.required = field.required || false;
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Event-Listener für Live-Validierung
|
|
|
|
input.addEventListener('input', () => {
|
|
|
|
validateField(field, input);
|
|
|
|
updatePreview();
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
input.addEventListener('blur', () => {
|
|
|
|
validateField(field, input);
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
fieldDiv.appendChild(label);
|
|
|
|
fieldDiv.appendChild(input);
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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 = '<i class="fas fa-image"></i><br>Vorschau';
|
|
|
|
imagePreview.appendChild(placeholder);
|
|
|
|
|
|
|
|
fieldDiv.appendChild(imagePreview);
|
|
|
|
|
|
|
|
// Bild-Validierung
|
|
|
|
input.addEventListener('input', () => {
|
|
|
|
validateImageUrl(input.value, field.name);
|
|
|
|
});
|
2025-08-04 06:14:47 +02:00
|
|
|
}
|
2025-08-04 09:24:53 +02:00
|
|
|
|
|
|
|
container.appendChild(fieldDiv);
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Klassen-Sektion generieren
|
|
|
|
function generateClassesSection(classes) {
|
|
|
|
const container = document.getElementById('classesContainer');
|
|
|
|
container.innerHTML = '';
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
const checkboxGroup = document.createElement('div');
|
|
|
|
checkboxGroup.className = 'checkbox-group';
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
Object.keys(classes).forEach(classKey => {
|
|
|
|
const checkboxItem = document.createElement('div');
|
|
|
|
checkboxItem.className = 'checkbox-item';
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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);
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
container.appendChild(checkboxGroup);
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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');
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Pflichtfeld-Prüfung
|
|
|
|
if (field.required && !value) {
|
|
|
|
showFieldError(formGroup, 'Dieses Feld ist erforderlich');
|
|
|
|
return false;
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
if (!value) return true; // Optional und leer
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Typ-spezifische Validierung
|
|
|
|
let isValid = true;
|
|
|
|
let errorMessage = '';
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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;
|
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
if (!isValid) {
|
|
|
|
showFieldError(formGroup, errorMessage);
|
|
|
|
return false;
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
2025-08-04 09:24:53 +02:00
|
|
|
|
|
|
|
// Erfolg anzeigen
|
|
|
|
showFieldSuccess(formGroup);
|
|
|
|
return true;
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Feld-Fehler anzeigen
|
|
|
|
function showFieldError(formGroup, message) {
|
|
|
|
formGroup.classList.add('error');
|
2025-08-04 06:14:47 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
const errorDiv = document.createElement('div');
|
|
|
|
errorDiv.className = 'error-message';
|
|
|
|
errorDiv.innerHTML = `<i class="fas fa-exclamation-triangle"></i> ${message}`;
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
formGroup.appendChild(errorDiv);
|
2025-08-04 06:14:47 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Feld-Erfolg anzeigen
|
|
|
|
function showFieldSuccess(formGroup) {
|
|
|
|
const successDiv = document.createElement('div');
|
|
|
|
successDiv.className = 'success-message';
|
|
|
|
successDiv.innerHTML = `<i class="fas fa-check"></i> Gültig`;
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
formGroup.appendChild(successDiv);
|
2025-08-04 06:51:25 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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 = '<div class="image-placeholder"><i class="fas fa-image"></i><br>Vorschau</div>';
|
|
|
|
return;
|
2025-08-04 06:51:25 +02:00
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Loading anzeigen
|
|
|
|
previewContainer.innerHTML = '<div class="image-placeholder"><i class="fas fa-spinner fa-spin"></i><br>Lade...</div>';
|
|
|
|
|
|
|
|
// Bild laden testen
|
|
|
|
const img = new Image();
|
|
|
|
img.onload = function() {
|
|
|
|
previewContainer.innerHTML = `<img src="${url}" alt="Vorschau">`;
|
|
|
|
updatePreview();
|
|
|
|
};
|
|
|
|
img.onerror = function() {
|
|
|
|
previewContainer.innerHTML = '<div class="image-placeholder"><i class="fas fa-exclamation-triangle"></i><br>Fehler beim Laden</div>';
|
|
|
|
};
|
|
|
|
img.src = url;
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
2025-08-04 06:51:25 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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;
|
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
const value = document.getElementById(field.name).value;
|
|
|
|
if (value) {
|
|
|
|
const fieldDiv = document.createElement('div');
|
|
|
|
fieldDiv.className = 'preview-field';
|
|
|
|
|
|
|
|
fieldDiv.innerHTML = `
|
|
|
|
<span class="label">${field.label}:</span>
|
|
|
|
<span class="value">${value}</span>
|
|
|
|
`;
|
|
|
|
|
|
|
|
detailsContainer.appendChild(fieldDiv);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Klassen
|
|
|
|
if (config.classes) {
|
|
|
|
const selectedClasses = Array.from(document.querySelectorAll('input[name="classes"]:checked'))
|
|
|
|
.map(cb => cb.value);
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
if (selectedClasses.length > 0) {
|
|
|
|
const classesDiv = document.createElement('div');
|
|
|
|
classesDiv.className = 'preview-field';
|
|
|
|
|
|
|
|
classesDiv.innerHTML = `
|
|
|
|
<span class="label">Klassen:</span>
|
|
|
|
<span class="value">${selectedClasses.join(', ')}</span>
|
|
|
|
`;
|
|
|
|
|
|
|
|
detailsContainer.appendChild(classesDiv);
|
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Form Event-Listener einrichten
|
|
|
|
function setupFormEventListeners() {
|
|
|
|
const form = document.getElementById('licenseForm');
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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;
|
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
showLoading();
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// An Client senden
|
|
|
|
fetch(`https://${GetParentResourceName()}/submitCustomLicense`, {
|
2025-08-04 06:53:42 +02:00
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
2025-08-04 09:24:53 +02:00
|
|
|
body: JSON.stringify({
|
|
|
|
licenseType: currentLicenseType,
|
|
|
|
targetId: currentTargetId,
|
|
|
|
customData: customData,
|
|
|
|
classes: classes
|
2025-08-04 06:53:42 +02:00
|
|
|
})
|
2025-08-04 09:24:53 +02:00
|
|
|
})
|
|
|
|
.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);
|
2025-08-04 06:53:42 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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');
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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 = `
|
|
|
|
<div class="license-item-header">
|
|
|
|
<div class="license-item-icon" style="background: ${config.color || '#2196f3'}">
|
|
|
|
<i class="${config.icon || 'fas fa-id-card'}"></i>
|
|
|
|
</div>
|
|
|
|
<div class="license-item-info">
|
|
|
|
<h4>${config.label || license.license_type}</h4>
|
|
|
|
<p>Ausgestellt: ${formatDate(license.issue_date)}</p>
|
|
|
|
${license.expire_date ? `<p>Gültig bis: ${formatDate(license.expire_date)}</p>` : '<p>Unbegrenzt gültig</p>'}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="license-item-actions">
|
|
|
|
<button class="btn btn-small btn-primary" onclick="viewLicenseDetails('${license.license_type}')">
|
|
|
|
<i class="fas fa-eye"></i> Anzeigen
|
|
|
|
</button>
|
|
|
|
<button class="btn btn-small btn-danger" onclick="revokeLicense('${license.license_type}')">
|
|
|
|
<i class="fas fa-trash"></i> Entziehen
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
|
|
|
grid.appendChild(item);
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
document.getElementById('playerLicensesDisplay').style.display = 'flex';
|
2025-08-04 06:14:47 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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 = `
|
|
|
|
<div class="license-header">
|
|
|
|
<h3>${config.label || license.license_type}</h3>
|
|
|
|
<p>${config.description || 'Offizielle Lizenz'}</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="license-content">
|
|
|
|
`;
|
|
|
|
|
|
|
|
// Foto
|
|
|
|
const photoUrl = customData.photo_url || customData.logo_url || currentData?.ui?.default_avatar || 'https://via.placeholder.com/120x150';
|
|
|
|
html += `
|
|
|
|
<div class="license-photo">
|
|
|
|
<img src="${photoUrl}" alt="Foto" onerror="this.src='https://via.placeholder.com/120x150/cccccc/666666?text=Kein+Foto'">
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
|
|
|
// Details
|
|
|
|
html += `<div class="license-details">`;
|
|
|
|
|
|
|
|
// Basis-Informationen
|
|
|
|
html += `
|
|
|
|
<div class="license-field">
|
|
|
|
<span class="label">Name:</span>
|
|
|
|
<span class="value">${license.holder_name || 'Unbekannt'}</span>
|
|
|
|
</div>
|
|
|
|
<div class="license-field">
|
|
|
|
<span class="label">Lizenz-Nr.:</span>
|
|
|
|
<span class="value">${license.id}</span>
|
|
|
|
</div>
|
|
|
|
<div class="license-field">
|
|
|
|
<span class="label">Ausgestellt:</span>
|
|
|
|
<span class="value">${formatDate(license.issue_date)}</span>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
|
|
|
if (license.expire_date) {
|
|
|
|
html += `
|
|
|
|
<div class="license-field">
|
|
|
|
<span class="label">Gültig bis:</span>
|
|
|
|
<span class="value">${formatDate(license.expire_date)}</span>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
html += `
|
|
|
|
<div class="license-field">
|
|
|
|
<span class="label">Ausgestellt von:</span>
|
|
|
|
<span class="value">${license.issued_by || 'System'}</span>
|
|
|
|
</div>
|
|
|
|
`;
|
2025-08-04 06:14:47 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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 += `
|
|
|
|
<div class="license-field">
|
|
|
|
<span class="label">${label}:</span>
|
|
|
|
<span class="value">${value}</span>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
html += `</div></div>`;
|
2025-08-04 06:53:42 +02:00
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Klassen
|
|
|
|
if (classes.length > 0) {
|
|
|
|
html += `
|
|
|
|
<div class="license-classes">
|
|
|
|
<h4>Berechtigte Klassen</h4>
|
|
|
|
<div class="classes-grid">
|
|
|
|
`;
|
|
|
|
|
|
|
|
classes.forEach(cls => {
|
|
|
|
html += `<div class="class-badge">${cls.key}</div>`;
|
|
|
|
});
|
|
|
|
|
|
|
|
html += `</div></div>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
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`, {
|
2025-08-04 06:14:47 +02:00
|
|
|
method: 'POST',
|
2025-08-04 09:24:53 +02:00
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2025-08-04 06:14:47 +02:00
|
|
|
body: JSON.stringify({})
|
2025-08-04 09:24:53 +02:00
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function closeCustomForm() {
|
|
|
|
document.getElementById('customLicenseForm').style.display = 'none';
|
2025-08-04 06:14:47 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function closeLicenseDisplay() {
|
|
|
|
document.getElementById('licenseDisplay').style.display = 'none';
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function closePlayerLicenses() {
|
|
|
|
document.getElementById('playerLicensesDisplay').style.display = 'none';
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function requestLicense() {
|
|
|
|
fetch(`https://${GetParentResourceName()}/requestLicense`, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
body: JSON.stringify({ targetId: currentTargetId })
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function requestMyLicense(licenseType) {
|
|
|
|
fetch(`https://${GetParentResourceName()}/requestMyLicense`, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
body: JSON.stringify({ licenseType: licenseType })
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function requestPlayerLicenses() {
|
|
|
|
fetch(`https://${GetParentResourceName()}/requestPlayerLicenses`, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
body: JSON.stringify({ targetId: currentTargetId })
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
function openLicenseCreation(licenseType) {
|
|
|
|
fetch(`https://${GetParentResourceName()}/openLicenseCreation`, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
licenseType: licenseType,
|
|
|
|
targetId: currentTargetId
|
|
|
|
})
|
|
|
|
});
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
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
|
2025-08-04 06:14:47 +02:00
|
|
|
document.addEventListener('keydown', function(event) {
|
|
|
|
if (event.key === 'Escape') {
|
2025-08-04 09:24:53 +02:00
|
|
|
// 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();
|
2025-08-04 06:53:42 +02:00
|
|
|
}
|
2025-08-04 06:14:47 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-08-04 09:24:53 +02:00
|
|
|
// Utility-Funktion für Resource-Name
|
|
|
|
function GetParentResourceName() {
|
|
|
|
return 'nordi_license'; // Ersetze mit deinem Resource-Namen
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('License System UI geladen (Erweiterte Version)');
|
2025-08-04 06:53:42 +02:00
|
|
|
|