diff --git a/api.php b/api.php index 32fc7ab..6b0f760 100644 --- a/api.php +++ b/api.php @@ -4235,6 +4235,37 @@ switch ($action) { } break; + // ==================== NETADMIN ==================== + + case 'netadmin_connections': + requireAuth(); + $companyId = requireCompany(); + try { + $connections = dbLoadAllConnections($companyId); + // Hae myös laitteet ja IPAM-tiedot aggregointia varten + $devices = _dbFetchAll("SELECT id, nimi, hallintaosoite, site_id, malli, ping_status FROM devices WHERE company_id = ?", [$companyId]); + $deviceMap = []; + foreach ($devices as $d) { $deviceMap[$d['nimi']] = $d; } + + // Rikasta liittymädata laitetiedoilla + foreach ($connections as &$conn) { + $deviceName = $conn['laite'] ?? ''; + if ($deviceName && isset($deviceMap[$deviceName])) { + $conn['device_info'] = $deviceMap[$deviceName]; + } + } + + echo json_encode([ + 'connections' => $connections, + 'total' => count($connections), + 'devices' => $devices + ]); + } catch (Exception $e) { + http_response_code(500); + echo json_encode(['error' => 'Liittymien haku epäonnistui: ' . $e->getMessage()]); + } + break; + // ==================== LAITETILAT ==================== case 'laitetilat': diff --git a/db.php b/db.php index 206433d..c458569 100644 --- a/db.php +++ b/db.php @@ -1878,3 +1878,20 @@ function dbDeleteLaitetilaFile(string $fileId): ?array { } return $file; } + +// ==================== NETADMIN ==================== + +function dbLoadAllConnections(string $companyId): array { + return _dbFetchAll(" + SELECT cc.*, + c.yritys AS customer_name, + c.yhteyshenkilö AS customer_contact, + c.puhelin AS customer_phone, + c.sahkoposti AS customer_email, + c.id AS customer_id + FROM customer_connections cc + JOIN customers c ON c.id = cc.customer_id + WHERE c.company_id = :companyId + ORDER BY cc.kaupunki, cc.asennusosoite + ", ['companyId' => $companyId]); +} diff --git a/index.html b/index.html index eed0fcf..abb07bb 100644 --- a/index.html +++ b/index.html @@ -84,6 +84,7 @@ + @@ -963,6 +964,52 @@ + +
+
+
+

🌐 NetAdmin — Liittymät

+
+ +
+
+
+ + + + +
+
+ + + + + + + + + + + + + + + +
AsiakasOsoiteKaupunkiNopeusVLANLaitePorttiIPHinta
+ +
+
+
+
@@ -1405,6 +1452,9 @@ + diff --git a/script.js b/script.js index 8fb5cf5..29d2771 100644 --- a/script.js +++ b/script.js @@ -200,7 +200,7 @@ async function showDashboard() { // Avaa oikea tabi URL-hashin perusteella (tai customers oletuks) const hash = window.location.hash.replace('#', ''); const [mainHash, subHash] = hash.split('/'); - const validTabs = ['customers', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'archive', 'changelog', 'support', 'users', 'settings', 'companies']; + const validTabs = ['customers', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'netadmin', 'archive', 'changelog', 'support', 'users', 'settings', 'companies']; const startTab = validTabs.includes(mainHash) ? mainHash : 'customers'; switchToTab(startTab, subHash); } @@ -265,6 +265,7 @@ function switchToTab(target, subTab) { if (target === 'support') { loadTickets(); showTicketListView(); if (document.getElementById('ticket-auto-refresh').checked) startTicketAutoRefresh(); } if (target === 'documents') { loadDocuments(); showDocsListView(); } if (target === 'laitetilat') { loadLaitetilat(); showLaitetilatListView(); } + if (target === 'netadmin') loadNetadmin(); if (target === 'users') loadUsers(); if (target === 'settings') loadSettings(); if (target === 'companies') loadCompaniesTab(); @@ -4394,6 +4395,106 @@ document.getElementById('btn-time-cancel')?.addEventListener('click', () => { }); document.getElementById('btn-time-save')?.addEventListener('click', () => addTimeEntry()); +// ==================== NETADMIN ==================== + +let netadminData = { connections: [], devices: [] }; + +async function loadNetadmin() { + try { + netadminData = await apiCall('netadmin_connections'); + populateNetadminFilters(); + renderNetadminTable(); + } catch (e) { console.error('NetAdmin lataus epäonnistui:', e); } +} + +function populateNetadminFilters() { + const conns = netadminData.connections || []; + + // Kaupungit + const cities = [...new Set(conns.map(c => c.kaupunki).filter(Boolean))].sort(); + const citySel = document.getElementById('netadmin-filter-city'); + const cityVal = citySel.value; + citySel.innerHTML = '' + + cities.map(c => ``).join(''); + citySel.value = cityVal; + + // Nopeudet + const speeds = [...new Set(conns.map(c => c.liittymanopeus).filter(Boolean))].sort(); + const speedSel = document.getElementById('netadmin-filter-speed'); + const speedVal = speedSel.value; + speedSel.innerHTML = '' + + speeds.map(s => ``).join(''); + speedSel.value = speedVal; + + // Laitteet + const devs = [...new Set(conns.map(c => c.laite).filter(Boolean))].sort(); + const devSel = document.getElementById('netadmin-filter-device'); + const devVal = devSel.value; + devSel.innerHTML = '' + + devs.map(d => ``).join(''); + devSel.value = devVal; +} + +function renderNetadminTable() { + const query = (document.getElementById('netadmin-search')?.value || '').toLowerCase().trim(); + const filterCity = document.getElementById('netadmin-filter-city')?.value || ''; + const filterSpeed = document.getElementById('netadmin-filter-speed')?.value || ''; + const filterDevice = document.getElementById('netadmin-filter-device')?.value || ''; + + let filtered = netadminData.connections || []; + + if (query) { + 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 + ].filter(Boolean).join(' ').toLowerCase(); + return searchStr.includes(query); + }); + } + if (filterCity) filtered = filtered.filter(c => c.kaupunki === filterCity); + if (filterSpeed) filtered = filtered.filter(c => c.liittymanopeus === filterSpeed); + if (filterDevice) filtered = filtered.filter(c => c.laite === filterDevice); + + const tbody = document.getElementById('netadmin-tbody'); + const noEl = document.getElementById('no-netadmin'); + const countEl = document.getElementById('netadmin-count'); + + countEl.textContent = `${filtered.length} / ${(netadminData.connections || []).length} liittymää`; + + if (filtered.length === 0) { + tbody.innerHTML = ''; + noEl.style.display = ''; + return; + } + noEl.style.display = 'none'; + + tbody.innerHTML = filtered.map(c => { + const addr = c.asennusosoite || '-'; + const deviceInfo = c.device_info; + const pingClass = deviceInfo?.ping_status === 'up' ? 'netadmin-status-up' : + deviceInfo?.ping_status === 'down' ? 'netadmin-status-down' : ''; + const deviceDisplay = c.laite ? `${esc(c.laite)}` : '-'; + + return ` + ${esc(c.customer_name || '-')} + ${esc(addr)} + ${esc(c.kaupunki || '-')} + ${esc(c.liittymanopeus || '-')} + ${esc(c.vlan || '-')} + ${deviceDisplay} + ${esc(c.portti || '-')} + ${esc(c.ip || '-')} + ${c.hinta ? parseFloat(c.hinta).toFixed(2) + ' €' : '-'} + `; + }).join(''); +} + +document.getElementById('netadmin-search')?.addEventListener('input', renderNetadminTable); +document.getElementById('netadmin-filter-city')?.addEventListener('change', renderNetadminTable); +document.getElementById('netadmin-filter-speed')?.addEventListener('change', renderNetadminTable); +document.getElementById('netadmin-filter-device')?.addEventListener('change', renderNetadminTable); + // ==================== DOKUMENTIT ==================== let allDocuments = []; @@ -4910,7 +5011,7 @@ document.getElementById('laitetila-edit-form')?.addEventListener('submit', async // ==================== MODUULIT ==================== -const ALL_MODULES = ['customers', 'support', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'archive', 'changelog', 'settings']; +const ALL_MODULES = ['customers', 'support', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'netadmin', 'archive', 'changelog', 'settings']; const DEFAULT_MODULES = ['customers', 'support', 'archive', 'changelog', 'settings']; function applyModules(modules) { diff --git a/style.css b/style.css index c17586b..321b3d0 100644 --- a/style.css +++ b/style.css @@ -1708,3 +1708,39 @@ span.empty { .btn-icon:hover { background: #fee2e2; } + +/* ==================== NETADMIN ==================== */ +.netadmin-speed { + display: inline-block; + padding: 2px 8px; + background: #dbeafe; + color: #1e40af; + border-radius: 4px; + font-size: 0.8rem; + font-weight: 600; +} + +.netadmin-status-up { + color: #059669; +} +.netadmin-status-up::before { + content: '●'; + margin-right: 4px; + font-size: 0.7rem; +} + +.netadmin-status-down { + color: #dc2626; +} +.netadmin-status-down::before { + content: '●'; + margin-right: 4px; + font-size: 0.7rem; +} + +#netadmin-table code { + background: #f3f4f6; + padding: 1px 5px; + border-radius: 3px; + font-size: 0.82rem; +}