Liittymien VLAN/Laite/IP-kentät hakevat nyt tiedot IPAM:sta ja laiterekisteristä
- NetAdmin liittymälomakkeen VLAN, Laite ja IP muutettu tekstikentistä dropdown-valikoiksi - Asiakasformin liittymäkentät samoin muutettu dropdown-valikoiksi - Dropdownit populoidaan IPAM:n VLANeista, IP-osoitteista ja Tekniikan laiterekisteristä - IP-dropdown ryhmittelee vapaat ja varatut IP:t optgroupeilla - Laite-dropdown näyttää ping-statuksen, hallintaosoitteen ja mallin - VLAN-dropdown näyttää VLAN ID:n, nimen ja sijainnin - Jos nykyinen arvo ei ole IPAM/laiterekisterissä, näytetään se (manuaalinen)-lisätekstillä - IPAM-tilan automaattipäivitys: kun liittymälle asetetaan IP, IPAM merkitsee sen varatuksi - Kun IP poistetaan tai vaihdetaan, vanha IP vapautetaan IPAM:ssa automaattisesti - API palauttaa nyt vlans ja ips -listat netadmin_connections-endpointissa Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
164
script.js
164
script.js
@@ -684,15 +684,70 @@ function createLiittymaRow(data = {}, index = 0) {
|
||||
<option value="36" ${data.sopimuskausi === '36' ? 'selected' : ''}>36 kk</option>
|
||||
</select></div>
|
||||
<div class="form-group"><label>Alkaen</label><input type="date" class="l-alkupvm" value="${esc(data.alkupvm || '')}"></div>
|
||||
<div class="form-group"><label>VLAN</label><input type="text" class="l-vlan" value="${esc(data.vlan || '')}" placeholder="esim. 100"></div>
|
||||
<div class="form-group"><label>Laite</label><input type="text" class="l-laite" value="${esc(data.laite || '')}" placeholder="esim. SW-CORE-01"></div>
|
||||
<div class="form-group"><label>VLAN</label><select class="l-vlan"><option value="">- Ei VLANia -</option></select></div>
|
||||
<div class="form-group"><label>Laite</label><select class="l-laite"><option value="">- Ei laitetta -</option></select></div>
|
||||
<div class="form-group"><label>Portti</label><input type="text" class="l-portti" value="${esc(data.portti || '')}" placeholder="esim. Gi0/1"></div>
|
||||
<div class="form-group"><label>IP</label><input type="text" class="l-ip" value="${esc(data.ip || '')}" placeholder="esim. 10.0.0.5"></div>
|
||||
<div class="form-group"><label>IP</label><select class="l-ip"><option value="">- Ei IP:tä -</option></select></div>
|
||||
</div>`;
|
||||
div.querySelector('.btn-remove-row').addEventListener('click', () => { div.remove(); renumberLiittymaRows(); });
|
||||
// Populoi dropdownit IPAM/laite-datasta
|
||||
populateLiittymaRowDropdowns(div, data);
|
||||
return div;
|
||||
}
|
||||
|
||||
// Populoi yksittäisen liittymärivin VLAN/Laite/IP dropdownit
|
||||
function populateLiittymaRowDropdowns(row, data = {}) {
|
||||
const vlanSel = row.querySelector('.l-vlan');
|
||||
const laiteSel = row.querySelector('.l-laite');
|
||||
const ipSel = row.querySelector('.l-ip');
|
||||
// Käytä netadminData:a tai fallback ipamData/devicesData:an
|
||||
const vlans = (netadminData.vlans && netadminData.vlans.length) ? netadminData.vlans : (ipamData || []).filter(e => e.tyyppi === 'vlan');
|
||||
const ips = (netadminData.ips && netadminData.ips.length) ? netadminData.ips : (ipamData || []).filter(e => e.tyyppi === 'ip');
|
||||
const devices = (netadminData.devices && netadminData.devices.length) ? netadminData.devices : (devicesData || []);
|
||||
// VLAN
|
||||
let vlanHtml = '<option value="">- Ei VLANia -</option>';
|
||||
[...vlans].sort((a, b) => (a.vlan_id || 0) - (b.vlan_id || 0)).forEach(v => {
|
||||
const val = String(v.vlan_id || '');
|
||||
const label = val + (v.nimi ? ` — ${v.nimi}` : '');
|
||||
vlanHtml += `<option value="${esc(val)}">${esc(label)}</option>`;
|
||||
});
|
||||
if (data.vlan && !vlans.some(v => String(v.vlan_id) === String(data.vlan))) {
|
||||
vlanHtml += `<option value="${esc(data.vlan)}">${esc(data.vlan)} (manuaalinen)</option>`;
|
||||
}
|
||||
vlanSel.innerHTML = vlanHtml;
|
||||
vlanSel.value = data.vlan || '';
|
||||
// Laite
|
||||
let laiteHtml = '<option value="">- Ei laitetta -</option>';
|
||||
[...devices].sort((a, b) => (a.nimi || '').localeCompare(b.nimi || '')).forEach(d => {
|
||||
const label = d.nimi + (d.hallintaosoite ? ` (${d.hallintaosoite})` : '');
|
||||
laiteHtml += `<option value="${esc(d.nimi)}">${esc(label)}</option>`;
|
||||
});
|
||||
if (data.laite && !devices.some(d => d.nimi === data.laite)) {
|
||||
laiteHtml += `<option value="${esc(data.laite)}">${esc(data.laite)} (manuaalinen)</option>`;
|
||||
}
|
||||
laiteSel.innerHTML = laiteHtml;
|
||||
laiteSel.value = data.laite || '';
|
||||
// IP
|
||||
let ipHtml = '<option value="">- Ei IP:tä -</option>';
|
||||
const free = ips.filter(i => i.tila === 'vapaa').sort((a, b) => (a.verkko || '').localeCompare(b.verkko || ''));
|
||||
const taken = ips.filter(i => i.tila === 'varattu').sort((a, b) => (a.verkko || '').localeCompare(b.verkko || ''));
|
||||
if (free.length) {
|
||||
ipHtml += '<optgroup label="Vapaat IP:t">';
|
||||
free.forEach(i => { ipHtml += `<option value="${esc(i.verkko)}">${esc(i.verkko)}${i.nimi ? ` — ${esc(i.nimi)}` : ''}</option>`; });
|
||||
ipHtml += '</optgroup>';
|
||||
}
|
||||
if (taken.length) {
|
||||
ipHtml += '<optgroup label="Varatut IP:t">';
|
||||
taken.forEach(i => { ipHtml += `<option value="${esc(i.verkko)}">${esc(i.verkko)}${i.asiakas ? ` ← ${esc(i.asiakas)}` : ''}</option>`; });
|
||||
ipHtml += '</optgroup>';
|
||||
}
|
||||
if (data.ip && !ips.some(i => i.verkko === data.ip)) {
|
||||
ipHtml += `<option value="${esc(data.ip)}">${esc(data.ip)} (manuaalinen)</option>`;
|
||||
}
|
||||
ipSel.innerHTML = ipHtml;
|
||||
ipSel.value = data.ip || '';
|
||||
}
|
||||
|
||||
function renumberLiittymaRows() {
|
||||
document.getElementById('liittymat-container').querySelectorAll('.liittyma-row').forEach((row, i) => {
|
||||
row.dataset.index = i;
|
||||
@@ -739,7 +794,7 @@ document.getElementById('btn-add').addEventListener('click', () => openCustomerF
|
||||
document.getElementById('modal-close').addEventListener('click', () => customerModal.style.display = 'none');
|
||||
document.getElementById('form-cancel').addEventListener('click', () => customerModal.style.display = 'none');
|
||||
|
||||
function openCustomerForm(customer = null) {
|
||||
async function openCustomerForm(customer = null) {
|
||||
const c = customer;
|
||||
document.getElementById('modal-title').textContent = c ? 'Muokkaa asiakasta' : 'Lisää asiakas';
|
||||
document.getElementById('form-submit').textContent = c ? 'Päivitä' : 'Tallenna';
|
||||
@@ -759,6 +814,8 @@ function openCustomerForm(customer = null) {
|
||||
document.getElementById('form-priority-emails').value = c ? (c.priority_emails || '') : '';
|
||||
document.getElementById('form-billing-same').checked = false;
|
||||
document.getElementById('billing-fields').style.display = 'block';
|
||||
// Lataa IPAM/laite-data dropdowendeja varten (jos ei vielä ladattu)
|
||||
await ensureIpamDevicesLoaded();
|
||||
const container = document.getElementById('liittymat-container');
|
||||
container.innerHTML = '';
|
||||
(c ? (c.liittymat || []) : [{}]).forEach((l, i) => container.appendChild(createLiittymaRow(l, i)));
|
||||
@@ -766,7 +823,27 @@ function openCustomerForm(customer = null) {
|
||||
document.getElementById('form-yritys').focus();
|
||||
}
|
||||
|
||||
function editCustomer(id) { const c = customers.find(x => x.id === id); if (c) openCustomerForm(c); }
|
||||
// Varmista IPAM/laite-data on ladattu dropdowneja varten
|
||||
async function ensureIpamDevicesLoaded() {
|
||||
try {
|
||||
// Jos netadminData:ssa ei ole IPAM-dataa, lataa suoraan
|
||||
if (!netadminData.vlans || !netadminData.vlans.length || !netadminData.devices || !netadminData.devices.length) {
|
||||
const [ipam, devices] = await Promise.all([
|
||||
apiCall('ipam'),
|
||||
apiCall('devices')
|
||||
]);
|
||||
if (!netadminData.vlans || !netadminData.vlans.length) {
|
||||
netadminData.vlans = ipam.filter(e => e.tyyppi === 'vlan');
|
||||
netadminData.ips = ipam.filter(e => e.tyyppi === 'ip');
|
||||
}
|
||||
if (!netadminData.devices || !netadminData.devices.length) {
|
||||
netadminData.devices = devices;
|
||||
}
|
||||
}
|
||||
} catch (e) { console.error('IPAM/laite-datan lataus epäonnistui:', e); }
|
||||
}
|
||||
|
||||
async function editCustomer(id) { const c = customers.find(x => x.id === id); if (c) await openCustomerForm(c); }
|
||||
|
||||
async function deleteCustomer(id, name) {
|
||||
if (!confirm(`Arkistoidaanko asiakas "${name}"?\n\nAsiakas siirretään arkistoon, josta sen voi palauttaa.`)) return;
|
||||
@@ -4518,7 +4595,7 @@ document.getElementById('btn-time-save')?.addEventListener('click', () => addTim
|
||||
|
||||
// ==================== NETADMIN ====================
|
||||
|
||||
let netadminData = { connections: [], devices: [] };
|
||||
let netadminData = { connections: [], devices: [], vlans: [], ips: [] };
|
||||
|
||||
async function loadNetadmin() {
|
||||
try {
|
||||
@@ -4615,6 +4692,74 @@ document.getElementById('netadmin-filter-city')?.addEventListener('change', rend
|
||||
document.getElementById('netadmin-filter-speed')?.addEventListener('change', renderNetadminTable);
|
||||
document.getElementById('netadmin-filter-device')?.addEventListener('change', renderNetadminTable);
|
||||
|
||||
// Populoi VLAN-dropdown IPAM VLANeista
|
||||
function populateVlanDropdown(selectEl, currentValue) {
|
||||
const vlans = netadminData.vlans || [];
|
||||
let html = '<option value="">- Ei VLANia -</option>';
|
||||
vlans.sort((a, b) => (a.vlan_id || 0) - (b.vlan_id || 0));
|
||||
vlans.forEach(v => {
|
||||
const val = String(v.vlan_id || '');
|
||||
const label = val + (v.nimi ? ` — ${v.nimi}` : '') + (v.site_name ? ` (${v.site_name})` : '');
|
||||
html += `<option value="${esc(val)}">${esc(label)}</option>`;
|
||||
});
|
||||
// Jos nykyinen arvo ei ole listalla, lisää se custom-optiona
|
||||
if (currentValue && !vlans.some(v => String(v.vlan_id) === String(currentValue))) {
|
||||
html += `<option value="${esc(currentValue)}">${esc(currentValue)} (manuaalinen)</option>`;
|
||||
}
|
||||
selectEl.innerHTML = html;
|
||||
selectEl.value = currentValue || '';
|
||||
}
|
||||
|
||||
// Populoi laite-dropdown
|
||||
function populateDeviceDropdown(selectEl, currentValue) {
|
||||
const devices = netadminData.devices || [];
|
||||
let html = '<option value="">- Ei laitetta -</option>';
|
||||
devices.sort((a, b) => (a.nimi || '').localeCompare(b.nimi || ''));
|
||||
devices.forEach(d => {
|
||||
const label = d.nimi + (d.hallintaosoite ? ` (${d.hallintaosoite})` : '') + (d.malli ? ` — ${d.malli}` : '');
|
||||
const pingDot = d.ping_status === 'up' ? '🟢 ' : d.ping_status === 'down' ? '🔴 ' : '';
|
||||
html += `<option value="${esc(d.nimi)}">${pingDot}${esc(label)}</option>`;
|
||||
});
|
||||
// Jos nykyinen arvo ei ole listalla, lisää se custom-optiona
|
||||
if (currentValue && !devices.some(d => d.nimi === currentValue)) {
|
||||
html += `<option value="${esc(currentValue)}">${esc(currentValue)} (manuaalinen)</option>`;
|
||||
}
|
||||
selectEl.innerHTML = html;
|
||||
selectEl.value = currentValue || '';
|
||||
}
|
||||
|
||||
// Populoi IP-dropdown IPAM IP-osoitteista
|
||||
function populateIpDropdown(selectEl, currentValue) {
|
||||
const ips = netadminData.ips || [];
|
||||
let html = '<option value="">- Ei IP:tä -</option>';
|
||||
// Ryhmittele: vapaat ensin, sitten varatut
|
||||
const free = ips.filter(i => i.tila === 'vapaa').sort((a, b) => (a.verkko || '').localeCompare(b.verkko || ''));
|
||||
const taken = ips.filter(i => i.tila === 'varattu').sort((a, b) => (a.verkko || '').localeCompare(b.verkko || ''));
|
||||
|
||||
if (free.length) {
|
||||
html += '<optgroup label="Vapaat IP:t">';
|
||||
free.forEach(i => {
|
||||
const label = i.verkko + (i.nimi ? ` — ${i.nimi}` : '') + (i.site_name ? ` (${i.site_name})` : '');
|
||||
html += `<option value="${esc(i.verkko)}">${esc(label)}</option>`;
|
||||
});
|
||||
html += '</optgroup>';
|
||||
}
|
||||
if (taken.length) {
|
||||
html += '<optgroup label="Varatut IP:t">';
|
||||
taken.forEach(i => {
|
||||
const label = i.verkko + (i.asiakas ? ` ← ${i.asiakas}` : '') + (i.nimi ? ` — ${i.nimi}` : '');
|
||||
html += `<option value="${esc(i.verkko)}">${esc(label)}</option>`;
|
||||
});
|
||||
html += '</optgroup>';
|
||||
}
|
||||
// Jos nykyinen arvo ei ole listalla, lisää se custom-optiona
|
||||
if (currentValue && !ips.some(i => i.verkko === currentValue)) {
|
||||
html += `<option value="${esc(currentValue)}">${esc(currentValue)} (manuaalinen)</option>`;
|
||||
}
|
||||
selectEl.innerHTML = html;
|
||||
selectEl.value = currentValue || '';
|
||||
}
|
||||
|
||||
async function openNetadminDetail(connId) {
|
||||
try {
|
||||
const conn = await apiCall(`netadmin_connection&id=${connId}`);
|
||||
@@ -4634,10 +4779,11 @@ async function openNetadminDetail(connId) {
|
||||
speedSel.insertBefore(opt, speedSel.lastElementChild);
|
||||
}
|
||||
speedSel.value = speed;
|
||||
document.getElementById('na-edit-vlan').value = conn.vlan || '';
|
||||
document.getElementById('na-edit-laite').value = conn.laite || '';
|
||||
// Populoi VLAN, Laite ja IP dropdownit IPAM/Tekniikka-datasta
|
||||
populateVlanDropdown(document.getElementById('na-edit-vlan'), conn.vlan || '');
|
||||
populateDeviceDropdown(document.getElementById('na-edit-laite'), conn.laite || '');
|
||||
populateIpDropdown(document.getElementById('na-edit-ip'), conn.ip || '');
|
||||
document.getElementById('na-edit-portti').value = conn.portti || '';
|
||||
document.getElementById('na-edit-ip').value = conn.ip || '';
|
||||
document.getElementById('netadmin-detail-modal').style.display = '';
|
||||
} catch (e) { alert('Liittymän avaus epäonnistui: ' + e.message); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user