1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_license/html/script.js
2025-08-04 09:24:53 +02:00

802 lines
25 KiB
JavaScript

// 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 = `
<i class="${config.icon}"></i>
<span>${config.label}</span>
`;
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 = '<i class="fas fa-image"></i><br>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 = `<i class="fas fa-exclamation-triangle"></i> ${message}`;
formGroup.appendChild(errorDiv);
}
// 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`;
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 = '<div class="image-placeholder"><i class="fas fa-image"></i><br>Vorschau</div>';
return;
}
// 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;
}
// 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 = `
<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);
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);
}
}
}
// 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 = `
<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);
});
}
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 = `
<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>
`;
// 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>`;
// 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`, {
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)');