Zuweisung über Kunden & Lizenzen geht
Dieser Commit ist enthalten in:
@@ -234,7 +234,7 @@
|
||||
|
||||
<!-- Modal für Ressourcen-Verwaltung -->
|
||||
<div class="modal fade" id="resourceManagementModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Ressourcen verwalten</h5>
|
||||
@@ -244,8 +244,11 @@
|
||||
<!-- Wird dynamisch gefüllt -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="me-auto">
|
||||
<span class="text-muted" id="selectionCounter">0 Ressourcen ausgewählt</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveResourceChanges()">
|
||||
<button type="button" class="btn btn-primary" onclick="saveResourceChanges()" id="saveResourcesBtn">
|
||||
<i class="bi bi-save"></i> Änderungen speichern
|
||||
</button>
|
||||
</div>
|
||||
@@ -310,6 +313,44 @@
|
||||
.btn-link {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* Multi-select improvements */
|
||||
select[multiple] {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #6c757d #f8f9fa;
|
||||
}
|
||||
|
||||
select[multiple]::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
select[multiple]::-webkit-scrollbar-track {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
select[multiple]::-webkit-scrollbar-thumb {
|
||||
background-color: #6c757d;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
select[multiple] option {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
select[multiple] option:checked {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
}
|
||||
|
||||
select[multiple] option:hover {
|
||||
background-color: #e9ecef;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
/* Modal improvements */
|
||||
.modal-xl {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -672,6 +713,9 @@ function showResourceManagement(licenseId) {
|
||||
const license = currentLicenses[licenseId];
|
||||
if (!license) return;
|
||||
|
||||
// Speichere License-ID im Modal für späteren Zugriff
|
||||
document.getElementById('resourceManagementModal').dataset.licenseId = licenseId;
|
||||
|
||||
// Lade aktuelle Ressourcen-Informationen
|
||||
fetch(`/api/license/${licenseId}/resources`)
|
||||
.then(response => response.json())
|
||||
@@ -775,7 +819,62 @@ function showResourceManagement(licenseId) {
|
||||
content += `
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<hr class="my-4">
|
||||
|
||||
<h6>Neue Ressourcen zuweisen</h6>
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Wählen Sie verfügbare Ressourcen aus, um sie dieser Lizenz zuzuweisen.
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">🌐 Domains hinzufügen</label>
|
||||
<div class="text-muted small mb-2">Mehrfachauswahl mit Strg/Cmd + Klick</div>
|
||||
<select id="availableDomains" class="form-select" size="10" multiple>
|
||||
<option value="" disabled>Lade verfügbare Domains...</option>
|
||||
</select>
|
||||
<div class="mt-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="selectAllInCategory('availableDomains')">
|
||||
Alle auswählen
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="deselectAllInCategory('availableDomains')">
|
||||
Auswahl aufheben
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">📡 IPv4-Adressen hinzufügen</label>
|
||||
<div class="text-muted small mb-2">Mehrfachauswahl mit Strg/Cmd + Klick</div>
|
||||
<select id="availableIpv4s" class="form-select" size="10" multiple>
|
||||
<option value="" disabled>Lade verfügbare IPs...</option>
|
||||
</select>
|
||||
<div class="mt-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="selectAllInCategory('availableIpv4s')">
|
||||
Alle auswählen
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="deselectAllInCategory('availableIpv4s')">
|
||||
Auswahl aufheben
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">📱 Telefonnummern hinzufügen</label>
|
||||
<div class="text-muted small mb-2">Mehrfachauswahl mit Strg/Cmd + Klick</div>
|
||||
<select id="availablePhones" class="form-select" size="10" multiple>
|
||||
<option value="" disabled>Lade verfügbare Nummern...</option>
|
||||
</select>
|
||||
<div class="mt-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="selectAllInCategory('availablePhones')">
|
||||
Alle auswählen
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="deselectAllInCategory('availablePhones')">
|
||||
Auswahl aufheben
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning mt-3">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
Das Quarantäne-setzen einer Ressource entfernt sie von der Lizenz und markiert sie als problematisch.
|
||||
</div>`;
|
||||
@@ -783,6 +882,20 @@ function showResourceManagement(licenseId) {
|
||||
document.getElementById('resourceManagementContent').innerHTML = content;
|
||||
const modal = new bootstrap.Modal(document.getElementById('resourceManagementModal'));
|
||||
modal.show();
|
||||
|
||||
// Lade verfügbare Ressourcen
|
||||
loadAvailableResources(licenseId);
|
||||
|
||||
// Add event listeners for selection changes after resources are loaded
|
||||
setTimeout(() => {
|
||||
['availableDomains', 'availableIpv4s', 'availablePhones'].forEach(selectId => {
|
||||
const select = document.getElementById(selectId);
|
||||
if (select) {
|
||||
select.addEventListener('change', updateSelectionCounter);
|
||||
}
|
||||
});
|
||||
updateSelectionCounter();
|
||||
}, 500);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -801,9 +914,187 @@ function quarantineResource(resourceId, resourceValue) {
|
||||
alert('Diese Funktion wird noch implementiert');
|
||||
}
|
||||
|
||||
// Dummy-Funktion für Speichern (wird später implementiert)
|
||||
// Lade verfügbare Ressourcen
|
||||
function loadAvailableResources(licenseId) {
|
||||
// Hole show_test Parameter aus der URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const showTest = urlParams.get('show_test') === 'true';
|
||||
|
||||
// Lade verfügbare Domains
|
||||
fetch(`/api/resources/check-availability?type=domain&count=200&show_test=${showTest}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const select = document.getElementById('availableDomains');
|
||||
select.innerHTML = '';
|
||||
if (data.available && data.available.length > 0) {
|
||||
data.available.forEach(resource => {
|
||||
const option = document.createElement('option');
|
||||
option.value = resource.id;
|
||||
option.textContent = resource.value;
|
||||
select.appendChild(option);
|
||||
});
|
||||
// Zeige Anzahl verfügbarer Ressourcen
|
||||
const label = select.parentElement.querySelector('.form-label');
|
||||
label.innerHTML = `🌐 Domains hinzufügen (${data.available.length} verfügbar)`;
|
||||
} else {
|
||||
select.innerHTML = '<option value="" disabled>Keine verfügbaren Domains</option>';
|
||||
}
|
||||
});
|
||||
|
||||
// Lade verfügbare IPv4s
|
||||
fetch(`/api/resources/check-availability?type=ipv4&count=200&show_test=${showTest}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const select = document.getElementById('availableIpv4s');
|
||||
select.innerHTML = '';
|
||||
if (data.available && data.available.length > 0) {
|
||||
data.available.forEach(resource => {
|
||||
const option = document.createElement('option');
|
||||
option.value = resource.id;
|
||||
option.textContent = resource.value;
|
||||
select.appendChild(option);
|
||||
});
|
||||
// Zeige Anzahl verfügbarer Ressourcen
|
||||
const label = select.parentElement.querySelector('.form-label');
|
||||
label.innerHTML = `📡 IPv4-Adressen hinzufügen (${data.available.length} verfügbar)`;
|
||||
} else {
|
||||
select.innerHTML = '<option value="" disabled>Keine verfügbaren IPv4-Adressen</option>';
|
||||
}
|
||||
});
|
||||
|
||||
// Lade verfügbare Telefonnummern
|
||||
fetch(`/api/resources/check-availability?type=phone&count=200&show_test=${showTest}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const select = document.getElementById('availablePhones');
|
||||
select.innerHTML = '';
|
||||
if (data.available && data.available.length > 0) {
|
||||
data.available.forEach(resource => {
|
||||
const option = document.createElement('option');
|
||||
option.value = resource.id;
|
||||
option.textContent = resource.value;
|
||||
select.appendChild(option);
|
||||
});
|
||||
// Zeige Anzahl verfügbarer Ressourcen
|
||||
const label = select.parentElement.querySelector('.form-label');
|
||||
label.innerHTML = `📱 Telefonnummern hinzufügen (${data.available.length} verfügbar)`;
|
||||
} else {
|
||||
select.innerHTML = '<option value="" disabled>Keine verfügbaren Telefonnummern</option>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Alle Optionen in einer Kategorie auswählen
|
||||
function selectAllInCategory(selectId) {
|
||||
const select = document.getElementById(selectId);
|
||||
for (let i = 0; i < select.options.length; i++) {
|
||||
if (!select.options[i].disabled) {
|
||||
select.options[i].selected = true;
|
||||
}
|
||||
}
|
||||
updateSelectionCounter();
|
||||
}
|
||||
|
||||
// Auswahl in einer Kategorie aufheben
|
||||
function deselectAllInCategory(selectId) {
|
||||
const select = document.getElementById(selectId);
|
||||
for (let i = 0; i < select.options.length; i++) {
|
||||
select.options[i].selected = false;
|
||||
}
|
||||
updateSelectionCounter();
|
||||
}
|
||||
|
||||
// Update selection counter
|
||||
function updateSelectionCounter() {
|
||||
const domainsCount = document.getElementById('availableDomains').selectedOptions.length;
|
||||
const ipv4sCount = document.getElementById('availableIpv4s').selectedOptions.length;
|
||||
const phonesCount = document.getElementById('availablePhones').selectedOptions.length;
|
||||
const totalCount = domainsCount + ipv4sCount + phonesCount;
|
||||
|
||||
const counter = document.getElementById('selectionCounter');
|
||||
if (counter) {
|
||||
counter.textContent = `${totalCount} Ressourcen ausgewählt`;
|
||||
|
||||
// Update save button state
|
||||
const saveBtn = document.getElementById('saveResourcesBtn');
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = totalCount === 0;
|
||||
if (totalCount === 0) {
|
||||
saveBtn.classList.add('disabled');
|
||||
} else {
|
||||
saveBtn.classList.remove('disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Speichere Ressourcen-Änderungen
|
||||
function saveResourceChanges() {
|
||||
alert('Diese Funktion wird noch implementiert');
|
||||
const modal = document.getElementById('resourceManagementModal');
|
||||
const licenseId = modal.dataset.licenseId || currentLicenses[Object.keys(currentLicenses)[0]].id;
|
||||
|
||||
// Sammle ausgewählte Ressourcen
|
||||
const selectedDomains = Array.from(document.getElementById('availableDomains').selectedOptions).map(opt => opt.value);
|
||||
const selectedIpv4s = Array.from(document.getElementById('availableIpv4s').selectedOptions).map(opt => opt.value);
|
||||
const selectedPhones = Array.from(document.getElementById('availablePhones').selectedOptions).map(opt => opt.value);
|
||||
|
||||
const allResources = [...selectedDomains, ...selectedIpv4s, ...selectedPhones];
|
||||
|
||||
if (allResources.length === 0) {
|
||||
alert('Bitte wählen Sie mindestens eine Ressource aus.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable save button and show loading state
|
||||
const saveBtn = document.getElementById('saveResourcesBtn');
|
||||
const originalBtnText = saveBtn.innerHTML;
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>Wird gespeichert...';
|
||||
|
||||
// API-Call zum Zuweisen der Ressourcen
|
||||
fetch('/api/resources/allocate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
license_id: licenseId,
|
||||
resource_ids: allResources
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Show success message with details
|
||||
const domainCount = selectedDomains.length;
|
||||
const ipv4Count = selectedIpv4s.length;
|
||||
const phoneCount = selectedPhones.length;
|
||||
|
||||
let message = `✅ ${data.allocated} Ressourcen erfolgreich zugewiesen!\n\n`;
|
||||
if (domainCount > 0) message += `🌐 ${domainCount} Domains\n`;
|
||||
if (ipv4Count > 0) message += `📡 ${ipv4Count} IPv4-Adressen\n`;
|
||||
if (phoneCount > 0) message += `📱 ${phoneCount} Telefonnummern`;
|
||||
|
||||
alert(message);
|
||||
|
||||
// Modal schließen und Ansicht aktualisieren
|
||||
bootstrap.Modal.getInstance(modal).hide();
|
||||
if (currentCustomerId) {
|
||||
loadCustomerLicenses(currentCustomerId);
|
||||
}
|
||||
} else {
|
||||
alert('❌ ' + (data.message || 'Fehler beim Zuweisen der Ressourcen'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error allocating resources:', error);
|
||||
alert('❌ Fehler beim Zuweisen der Ressourcen');
|
||||
})
|
||||
.finally(() => {
|
||||
// Restore button state
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.innerHTML = originalBtnText;
|
||||
});
|
||||
}
|
||||
|
||||
// Geräte-Verwaltung anzeigen
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren