diff --git a/api.php b/api.php
index ff92a6e..551454e 100644
--- a/api.php
+++ b/api.php
@@ -4384,12 +4384,33 @@ switch ($action) {
$vlans = array_values(array_filter($ipamAll, fn($e) => $e['tyyppi'] === 'vlan'));
$ips = array_values(array_filter($ipamAll, fn($e) => $e['tyyppi'] === 'ip' || $e['tyyppi'] === 'subnet'));
- // Rikasta liittymädata laitetiedoilla
+ // Rikasta liittymädata laitetiedoilla ja IPAM-tiedoilla
+ // Rakenna IPAM-lookup asiakkaan nimen perusteella
+ $ipamByCustomer = [];
+ foreach ($ipamAll as $entry) {
+ $asiakas = $entry['asiakas'] ?? '';
+ if ($asiakas) {
+ $ipamByCustomer[$asiakas][] = $entry;
+ }
+ }
+
foreach ($connections as &$conn) {
$deviceName = $conn['laite'] ?? '';
if ($deviceName && isset($deviceMap[$deviceName])) {
$conn['device_info'] = $deviceMap[$deviceName];
}
+ // Auto-populate VLAN ja IP IPAMista asiakkaan nimen perusteella
+ $custName = $conn['customer_name'] ?? '';
+ $ipamEntries = $ipamByCustomer[$custName] ?? [];
+ $conn['ipam_vlans'] = [];
+ $conn['ipam_ips'] = [];
+ foreach ($ipamEntries as $ie) {
+ if ($ie['tyyppi'] === 'vlan') {
+ $conn['ipam_vlans'][] = ['vlan_id' => $ie['vlan_id'], 'nimi' => $ie['nimi'], 'site_name' => $ie['site_name'] ?? ''];
+ } elseif ($ie['tyyppi'] === 'ip' || $ie['tyyppi'] === 'subnet') {
+ $conn['ipam_ips'][] = ['verkko' => $ie['verkko'], 'nimi' => $ie['nimi'], 'tila' => $ie['tila'], 'site_name' => $ie['site_name'] ?? ''];
+ }
+ }
}
echo json_encode([
@@ -4421,6 +4442,22 @@ switch ($action) {
echo json_encode(['error' => 'Liittymää ei löytynyt']);
break;
}
+ // Lisää IPAM-data asiakkaan nimen perusteella
+ $ipamAll = dbLoadIpam($companyId);
+ $custName = $conn['customer_name'] ?? '';
+ $conn['ipam_vlans'] = [];
+ $conn['ipam_ips'] = [];
+ if ($custName) {
+ foreach ($ipamAll as $ie) {
+ $asiakas = $ie['asiakas'] ?? '';
+ if ($asiakas !== $custName) continue;
+ if ($ie['tyyppi'] === 'vlan') {
+ $conn['ipam_vlans'][] = ['vlan_id' => $ie['vlan_id'], 'nimi' => $ie['nimi'], 'site_name' => $ie['site_name'] ?? ''];
+ } elseif ($ie['tyyppi'] === 'ip' || $ie['tyyppi'] === 'subnet') {
+ $conn['ipam_ips'][] = ['verkko' => $ie['verkko'], 'nimi' => $ie['nimi'], 'tila' => $ie['tila'], 'site_name' => $ie['site_name'] ?? ''];
+ }
+ }
+ }
echo json_encode($conn);
} catch (Exception $e) {
http_response_code(500);
diff --git a/db.php b/db.php
index 2c0b69e..750789b 100644
--- a/db.php
+++ b/db.php
@@ -616,6 +616,7 @@ function initDatabase(): void {
"ALTER TABLE document_versions ADD COLUMN content MEDIUMTEXT DEFAULT NULL AFTER mime_type",
"ALTER TABLE devices ADD COLUMN laitetila_id VARCHAR(20) DEFAULT NULL AFTER site_id",
"ALTER TABLE document_folders ADD COLUMN customer_id VARCHAR(20) DEFAULT NULL AFTER company_id",
+ "ALTER TABLE customer_connections ADD COLUMN gateway_device_id VARCHAR(20) DEFAULT NULL AFTER ip",
];
foreach ($alters as $sql) {
try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa / jo ajettu */ }
@@ -2051,9 +2052,13 @@ function dbLoadAllConnections(string $companyId): array {
c.yhteyshenkilö AS customer_contact,
c.puhelin AS customer_phone,
c.sahkoposti AS customer_email,
- c.id AS customer_id
+ c.id AS customer_id,
+ gw.nimi AS gateway_name,
+ gw.hallintaosoite AS gateway_ip,
+ gw.malli AS gateway_model
FROM customer_connections cc
JOIN customers c ON c.id = cc.customer_id
+ LEFT JOIN devices gw ON cc.gateway_device_id = gw.id
WHERE c.company_id = :companyId
ORDER BY cc.kaupunki, cc.asennusosoite
", ['companyId' => $companyId]);
@@ -2061,9 +2066,11 @@ function dbLoadAllConnections(string $companyId): array {
function dbLoadConnection(int $connectionId): ?array {
return _dbFetchOne("
- SELECT cc.*, c.yritys AS customer_name, c.company_id
+ SELECT cc.*, c.yritys AS customer_name, c.company_id,
+ gw.nimi AS gateway_name, gw.hallintaosoite AS gateway_ip, gw.malli AS gateway_model
FROM customer_connections cc
JOIN customers c ON c.id = cc.customer_id
+ LEFT JOIN devices gw ON cc.gateway_device_id = gw.id
WHERE cc.id = ?
", [$connectionId]);
}
@@ -2071,7 +2078,8 @@ function dbLoadConnection(int $connectionId): ?array {
function dbUpdateConnection(int $connectionId, array $data): void {
_dbExecute("UPDATE customer_connections SET
liittymanopeus = ?, vlan = ?, laite = ?, portti = ?, ip = ?,
- asennusosoite = ?, postinumero = ?, kaupunki = ?
+ asennusosoite = ?, postinumero = ?, kaupunki = ?,
+ gateway_device_id = ?
WHERE id = ?", [
$data['liittymanopeus'] ?? '',
$data['vlan'] ?? '',
@@ -2081,6 +2089,7 @@ function dbUpdateConnection(int $connectionId, array $data): void {
$data['asennusosoite'] ?? '',
$data['postinumero'] ?? '',
$data['kaupunki'] ?? '',
+ !empty($data['gateway_device_id']) ? $data['gateway_device_id'] : null,
$connectionId
]);
}
diff --git a/index.html b/index.html
index 10d20cd..7a6cb61 100644
--- a/index.html
+++ b/index.html
@@ -923,6 +923,7 @@
Laite |
Portti |
IP |
+ Gateway |
@@ -1002,6 +1003,14 @@
+
diff --git a/script.js b/script.js
index a118d90..a34d2c2 100644
--- a/script.js
+++ b/script.js
@@ -4539,7 +4539,7 @@ function renderNetadminTable() {
filtered = filtered.filter(c => {
const searchStr = [
c.customer_name, c.asennusosoite, c.kaupunki, c.postinumero,
- c.liittymanopeus, c.vlan, c.laite, c.portti, c.ip
+ c.liittymanopeus, c.vlan, c.laite, c.portti, c.ip, c.gateway_name
].filter(Boolean).join(' ').toLowerCase();
return searchStr.includes(query);
});
@@ -4568,15 +4568,35 @@ function renderNetadminTable() {
deviceInfo?.ping_status === 'down' ? 'netadmin-status-down' : '';
const deviceDisplay = c.laite ? `${esc(c.laite)}` : '-';
+ // VLAN: näytä tallennettu arvo, tai IPAM:sta haetut
+ let vlanDisplay = esc(c.vlan || '');
+ if (!c.vlan && c.ipam_vlans && c.ipam_vlans.length > 0) {
+ vlanDisplay = c.ipam_vlans.map(v =>
+ `${esc(String(v.vlan_id))}`
+ ).join(', ');
+ }
+
+ // IP: näytä tallennettu arvo, tai IPAM:sta haetut
+ let ipDisplay = c.ip ? `${esc(c.ip)}` : '';
+ if (!c.ip && c.ipam_ips && c.ipam_ips.length > 0) {
+ ipDisplay = c.ipam_ips.map(i =>
+ `${esc(i.verkko)}`
+ ).join(', ');
+ }
+
+ // Gateway
+ const gwDisplay = c.gateway_name ? esc(c.gateway_name) : '-';
+
return `
| ${esc(c.customer_name || '-')} |
${esc(addr)} |
${esc(c.kaupunki || '-')} |
${esc(c.liittymanopeus || '-')} |
- ${esc(c.vlan || '-')} |
+ ${vlanDisplay || '-'} |
${deviceDisplay} |
${esc(c.portti || '-')} |
- ${esc(c.ip || '-')} |
+ ${ipDisplay || '-'} |
+ ${gwDisplay} |
`;
}).join('');
}
@@ -4758,6 +4778,20 @@ function getIpComboOptions(source) {
return items;
}
+// Rakennetaan Gateway-combobox optiot (kaikki laitteet devices-listasta)
+function getGatewayComboOptions() {
+ const devices = netadminData.devices || [];
+ return [...devices].sort((a, b) => (a.nimi || '').localeCompare(b.nimi || '')).map(d => {
+ const pingDot = d.ping_status === 'up' ? '🟢' : d.ping_status === 'down' ? '🔴' : '';
+ return {
+ value: String(d.id),
+ label: (pingDot ? pingDot + ' ' : '') + d.nimi,
+ sub: [d.hallintaosoite, d.malli].filter(Boolean).join(' — '),
+ searchStr: `${d.nimi} ${d.hallintaosoite || ''} ${d.malli || ''} ${d.funktio || ''} ${d.site_name || ''}`,
+ };
+ });
+}
+
async function openNetadminDetail(connId) {
try {
const conn = await apiCall(`netadmin_connection&id=${connId}`);
@@ -4777,11 +4811,44 @@ async function openNetadminDetail(connId) {
speedSel.insertBefore(opt, speedSel.lastElementChild);
}
speedSel.value = speed;
- // Populoi VLAN, Laite ja IP hakukentät IPAM/Tekniikka-datasta
- initCombo(document.getElementById('na-combo-vlan'), getVlanComboOptions(), conn.vlan || '');
+ // Populoi VLAN, Laite ja IP hakukentät — yhdistä IPAM-data asiakkaan tietoihin
+ // Jos asiakkaalla on IPAM VLANeja, näytä ne ensin
+ let vlanOptions = getVlanComboOptions();
+ if (conn.ipam_vlans && conn.ipam_vlans.length > 0) {
+ const ipamVlanOpts = conn.ipam_vlans.map(v => ({
+ value: String(v.vlan_id || ''),
+ label: String(v.vlan_id || '') + (v.nimi ? ` — ${v.nimi}` : ''),
+ sub: v.site_name || '',
+ badge: 'IPAM', badgeClass: 'free',
+ group: '📌 Asiakkaan VLANit (IPAM)',
+ searchStr: `${v.vlan_id} ${v.nimi || ''} ${v.site_name || ''}`,
+ }));
+ vlanOptions = [...ipamVlanOpts, ...vlanOptions];
+ }
+ initCombo(document.getElementById('na-combo-vlan'), vlanOptions, conn.vlan || '');
+
initCombo(document.getElementById('na-combo-laite'), getDeviceComboOptions(), conn.laite || '');
- initCombo(document.getElementById('na-combo-ip'), getIpComboOptions(), conn.ip || '');
+
+ // Jos asiakkaalla on IPAM IP:itä, näytä ne ensin
+ let ipOptions = getIpComboOptions();
+ if (conn.ipam_ips && conn.ipam_ips.length > 0) {
+ const ipamIpOpts = conn.ipam_ips.map(i => ({
+ value: i.verkko,
+ label: i.verkko,
+ sub: [i.nimi, i.site_name].filter(Boolean).join(' — '),
+ badge: 'IPAM', badgeClass: 'free',
+ group: '📌 Asiakkaan IP:t (IPAM)',
+ searchStr: `${i.verkko} ${i.nimi || ''} ${i.site_name || ''}`,
+ }));
+ ipOptions = [...ipamIpOpts, ...ipOptions];
+ }
+ initCombo(document.getElementById('na-combo-ip'), ipOptions, conn.ip || '');
+
document.getElementById('na-edit-portti').value = conn.portti || '';
+
+ // Gateway-laitevalitsin
+ initCombo(document.getElementById('na-combo-gateway'), getGatewayComboOptions(), conn.gateway_device_id ? String(conn.gateway_device_id) : '');
+
document.getElementById('netadmin-detail-modal').style.display = '';
} catch (e) { alert('Liittymän avaus epäonnistui: ' + e.message); }
}
@@ -4800,6 +4867,7 @@ document.getElementById('netadmin-detail-form')?.addEventListener('submit', asyn
e.preventDefault();
const connId = document.getElementById('na-edit-id').value;
try {
+ const gwVal = document.getElementById('na-edit-gateway').value;
await apiCall('netadmin_connection_update', 'POST', {
id: parseInt(connId),
asennusosoite: document.getElementById('na-edit-osoite').value,
@@ -4809,7 +4877,8 @@ document.getElementById('netadmin-detail-form')?.addEventListener('submit', asyn
vlan: document.getElementById('na-edit-vlan').value,
laite: document.getElementById('na-edit-laite').value,
portti: document.getElementById('na-edit-portti').value,
- ip: document.getElementById('na-edit-ip').value
+ ip: document.getElementById('na-edit-ip').value,
+ gateway_device_id: gwVal ? parseInt(gwVal) : null
});
closeNetadminDetail();
loadNetadmin();
diff --git a/style.css b/style.css
index a828f3e..b668b11 100644
--- a/style.css
+++ b/style.css
@@ -1873,6 +1873,16 @@ span.empty {
font-size: 0.7rem;
}
+.ipam-hint {
+ background: #f0fdf4;
+ color: #166534;
+ padding: 1px 6px;
+ border-radius: 4px;
+ font-size: 0.8rem;
+ border: 1px solid #bbf7d0;
+ cursor: help;
+}
+
#netadmin-table code {
background: #f3f4f6;
padding: 1px 5px;