diff --git a/resources/[tools]/nordi_license/client/main.lua b/resources/[tools]/nordi_license/client/main.lua index 1d97b77e5..1d649e49f 100644 --- a/resources/[tools]/nordi_license/client/main.lua +++ b/resources/[tools]/nordi_license/client/main.lua @@ -425,10 +425,17 @@ openRevokeLicenseMenu = function(targetId, targetName, licenses) lib.showContext('revoke_license') end --- Manual License Entry Menu +-- Manual License Entry Menu (Vollständig manuell) openManualLicenseEntry = function(targetId, targetName) debugPrint("Öffne manuelle Lizenz-Eingabe für: " .. targetName) + -- Aktuelles Datum für Standardwerte + local currentDate = os.date("%d.%m.%Y") + + -- Berechne Standardablaufdatum (1 Jahr später) + local day, month, year = currentDate:match("(%d+)%.(%d+)%.(%d+)") + local defaultExpireDate = day .. "." .. month .. "." .. (tonumber(year) + 1) + local input = lib.inputDialog('Lizenz manuell ausstellen', { {type = 'select', label = 'Lizenztyp', options = getLicenseTypeOptions()}, {type = 'input', label = 'Name', default = targetName}, @@ -438,30 +445,44 @@ openManualLicenseEntry = function(targetId, targetName) {value = 'female', label = 'Weiblich'}, {value = 'other', label = 'Divers'} }}, - {type = 'date', label = 'Ausstellungsdatum', default = os.date("%Y-%m-%d")}, - {type = 'date', label = 'Ablaufdatum', default = os.date("%Y-%m-%d", os.time() + 365*24*60*60)}, + {type = 'input', label = 'Ausstellungsdatum', description = 'Format: DD.MM.YYYY', default = currentDate}, + {type = 'input', label = 'Ablaufdatum', description = 'Format: DD.MM.YYYY', default = defaultExpireDate}, + {type = 'input', label = 'Klassen (kommagetrennt)', description = 'z.B. A,B,C', default = ''}, + {type = 'input', label = 'Bemerkungen', default = ''}, {type = 'checkbox', label = 'Foto aufnehmen?'} }) if not input then return end local licenseType = input[1] + + -- Klassen verarbeiten + local classes = {} + if input[7] and input[7] ~= "" then + for class in string.gmatch(input[7], '([^,]+)') do + table.insert(classes, class:match("^%s*(.-)%s*$")) -- Leerzeichen entfernen + end + end + local licenseData = { name = input[2], birthday = input[3], gender = input[4], - issue_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[5] / 1000))), - expire_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[6] / 1000))), + issue_date = input[5], + expire_date = input[6], + classes = classes, + notes = input[8], license_type = licenseType } - if input[7] then -- Take photo + if input[9] then -- Foto aufnehmen TriggerEvent('license-system:client:openCamera', targetId, licenseData) else TriggerServerEvent('license-system:server:issueManualLicense', targetId, licenseData) end end + -- License Reactivation Menu openReactivateLicenseMenu = function(targetId, targetName) debugPrint("Öffne Lizenz-Reaktivierungs-Menü für: " .. targetName) diff --git a/resources/[tools]/nordi_license/html/script.js b/resources/[tools]/nordi_license/html/script.js index eca5c8326..67a71241b 100644 --- a/resources/[tools]/nordi_license/html/script.js +++ b/resources/[tools]/nordi_license/html/script.js @@ -19,7 +19,7 @@ window.addEventListener('message', function(event) { } }); -// Lizenz anzeigen +// In der showLicense Funktion in script.js function showLicense(data) { currentLicense = data; const container = document.getElementById('license-container'); @@ -69,121 +69,10 @@ function showLicense(data) { // 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 +// Erweitere die prepareBackSide Funktion um Bemerkungen anzuzeigen function prepareBackSide(license) { const classesGrid = document.getElementById('classes-grid'); const restrictionsList = document.getElementById('restrictions-list'); @@ -192,7 +81,7 @@ function prepareBackSide(license) { // Klassen-Grid für Führerschein if (license.license_type === 'drivers_license' && license.classes) { try { - const classes = JSON.parse(license.classes); + const classes = Array.isArray(license.classes) ? license.classes : JSON.parse(license.classes); classesGrid.innerHTML = ''; const classDescriptions = { @@ -207,15 +96,19 @@ function prepareBackSide(license) { '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); - }); + if (classes && classes.length > 0) { + classes.forEach(cls => { + const classItem = document.createElement('div'); + classItem.className = 'class-item'; + classItem.innerHTML = ` +
${cls}
+
${classDescriptions[cls] || 'Unbekannt'}
+ `; + classesGrid.appendChild(classItem); + }); + } else { + classesGrid.innerHTML = '

Keine Klassen verfügbar

'; + } } catch (e) { classesGrid.innerHTML = '

Keine Klassen verfügbar

'; } @@ -230,6 +123,7 @@ function prepareBackSide(license) { notesText.textContent = license.notes || 'Keine besonderen Bemerkungen'; } + // Karte drehen function flipCard() { const frontSide = document.querySelector('.license-content, .license-footer'); diff --git a/resources/[tools]/nordi_license/server/main.lua b/resources/[tools]/nordi_license/server/main.lua index 00ee16c85..2a7925a33 100644 --- a/resources/[tools]/nordi_license/server/main.lua +++ b/resources/[tools]/nordi_license/server/main.lua @@ -805,7 +805,7 @@ RegisterNetEvent('license-system:server:reactivateLicense', function(targetId, l end end) --- EVENT HANDLER: Manuelle Lizenz ausstellen +-- EVENT HANDLER: Manuelle Lizenz ausstellen (Erweitert) RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, licenseData) local src = source debugPrint("=== Event: issueManualLicense ===") @@ -833,29 +833,29 @@ RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, local targetCitizenId = targetPlayer.PlayerData.citizenid local issuerCitizenId = issuerPlayer.PlayerData.citizenid - -- Check if license type is valid + -- Prüfen ob Lizenztyp gültig ist if not Config.LicenseTypes[licenseData.license_type] then TriggerClientEvent('QBCore:Notify', src, 'Ungültiger Lizenztyp!', 'error') return end - -- Save photo if provided + -- Foto speichern falls vorhanden if licenseData.photo_url then - -- Here you would save the photo to your storage system - -- For example, to a folder or database - -- This is just a placeholder debugPrint("Foto für Lizenz vorhanden") end - -- First deactivate any existing licenses of this type + -- Alte Lizenzen deaktivieren local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?" MySQL.query.await(deactivateQuery, {targetCitizenId, licenseData.license_type}) - -- Insert into database with manual data + -- Klassen zu JSON konvertieren + local classesJson = json.encode(licenseData.classes or {}) + + -- In Datenbank einfügen mit manuellen Daten local query = [[ INSERT INTO player_licenses - (citizenid, license_type, name, birthday, gender, issue_date, expire_date, issued_by, is_active, photo_url, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?) + (citizenid, license_type, name, birthday, gender, issue_date, expire_date, issued_by, is_active, classes, photo_url, notes, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?) ]] local createdAt = os.time() @@ -869,25 +869,27 @@ RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, licenseData.issue_date, licenseData.expire_date, issuerCitizenId, + classesJson, licenseData.photo_url or '', + licenseData.notes or '', createdAt } local result = MySQL.insert.await(query, insertData) if result then - -- Invalidate cache + -- Cache invalidieren invalidateCache(targetCitizenId) local targetName = getPlayerName(targetId) local issuerName = getPlayerName(src) local config = Config.LicenseTypes[licenseData.license_type] - -- Notifications + -- Benachrichtigungen TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success') TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success') - -- Events + -- Events senden TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseData.license_type) TriggerClientEvent('license-system:client:refreshMenu', src) @@ -898,6 +900,7 @@ RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, end end) + -- EVENT HANDLER: Alle Lizenzen eines Spielers anfordern (inkl. inaktive) RegisterNetEvent('license-system:server:requestAllPlayerLicenses', function(targetId, includeInactive) local src = source