Yhdistä Sijainnit ja Laitetilat samaksi konseptiksi
Sijainnit (sites) ja Laitetilat olivat käytännössä sama asia. Nyt kaikki hallitaan Laitetilat-välilehdeltä: - DB-migraatio kopioi vanhat sites → laitetilat (sama ID säilyy) - Laitteiden site_id päivitetty automaattisesti laitetila_id:ksi - IPAM JOINaa nyt laitetilat-taulua sites:n sijaan - Sijainnit sub-tab poistettu Tekniikasta - Laiteformissa yksi "Sijainti / Laitetila" dropdown - Sites API-endpointit poistettu (sites palauttaa laitetilat) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
47
api.php
47
api.php
@@ -1882,47 +1882,12 @@ switch ($action) {
|
|||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ---------- SIJAINNIT (SITES) ----------
|
// ---------- SIJAINNIT (SITES) — YHDISTETTY LAITETILOIHIN ----------
|
||||||
|
// sites-endpoint palauttaa nyt laitetilat (taaksepäin yhteensopivuus)
|
||||||
case 'sites':
|
case 'sites':
|
||||||
requireAuth();
|
requireAuth();
|
||||||
$companyId = requireCompany();
|
$companyId = requireCompany();
|
||||||
echo json_encode(dbLoadSites($companyId));
|
echo json_encode(dbLoadLaitetilat($companyId));
|
||||||
break;
|
|
||||||
|
|
||||||
case 'site_save':
|
|
||||||
requireAdmin();
|
|
||||||
$companyId = requireCompany();
|
|
||||||
if ($method !== 'POST') break;
|
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
|
||||||
$site = [
|
|
||||||
'id' => $input['id'] ?? generateId(),
|
|
||||||
'nimi' => trim($input['nimi'] ?? ''),
|
|
||||||
'osoite' => trim($input['osoite'] ?? ''),
|
|
||||||
'kaupunki' => trim($input['kaupunki'] ?? ''),
|
|
||||||
];
|
|
||||||
if (empty($site['nimi'])) {
|
|
||||||
http_response_code(400);
|
|
||||||
echo json_encode(['error' => 'Sijainnin nimi vaaditaan']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dbSaveSite($companyId, $site);
|
|
||||||
dbAddLog($companyId, currentUser(), 'site_save', $site['id'], $site['nimi'], (isset($input['id']) ? 'Muokkasi' : 'Lisäsi') . ' sijainnin');
|
|
||||||
echo json_encode($site);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'site_delete':
|
|
||||||
requireAdmin();
|
|
||||||
$companyId = requireCompany();
|
|
||||||
if ($method !== 'POST') break;
|
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
|
||||||
$id = $input['id'] ?? '';
|
|
||||||
// Hae nimi logitusta varten
|
|
||||||
$sites = dbLoadSites($companyId);
|
|
||||||
$siteName = '';
|
|
||||||
foreach ($sites as $s) { if ($s['id'] === $id) { $siteName = $s['nimi']; break; } }
|
|
||||||
dbDeleteSite($id);
|
|
||||||
dbAddLog($companyId, currentUser(), 'site_delete', $id, $siteName, 'Poisti sijainnin');
|
|
||||||
echo json_encode(['success' => true]);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ---------- LAITTEET (DEVICES) ----------
|
// ---------- LAITTEET (DEVICES) ----------
|
||||||
@@ -1943,7 +1908,7 @@ switch ($action) {
|
|||||||
'nimi' => trim($input['nimi'] ?? ''),
|
'nimi' => trim($input['nimi'] ?? ''),
|
||||||
'hallintaosoite' => trim($input['hallintaosoite'] ?? ''),
|
'hallintaosoite' => trim($input['hallintaosoite'] ?? ''),
|
||||||
'serial' => trim($input['serial'] ?? ''),
|
'serial' => trim($input['serial'] ?? ''),
|
||||||
'site_id' => $input['site_id'] ?? null,
|
'site_id' => null,
|
||||||
'laitetila_id' => $input['laitetila_id'] ?? null,
|
'laitetila_id' => $input['laitetila_id'] ?? null,
|
||||||
'funktio' => trim($input['funktio'] ?? ''),
|
'funktio' => trim($input['funktio'] ?? ''),
|
||||||
'tyyppi' => trim($input['tyyppi'] ?? ''),
|
'tyyppi' => trim($input['tyyppi'] ?? ''),
|
||||||
@@ -1979,7 +1944,7 @@ switch ($action) {
|
|||||||
// Päivitä olemassa oleva: merkitse varatuksi laitteelle
|
// Päivitä olemassa oleva: merkitse varatuksi laitteelle
|
||||||
$existing['tila'] = 'varattu';
|
$existing['tila'] = 'varattu';
|
||||||
$existing['nimi'] = $device['nimi'];
|
$existing['nimi'] = $device['nimi'];
|
||||||
$existing['site_id'] = $device['site_id'];
|
$existing['site_id'] = $device['laitetila_id'];
|
||||||
$existing['muokattu'] = date('Y-m-d H:i:s');
|
$existing['muokattu'] = date('Y-m-d H:i:s');
|
||||||
$existing['muokkaaja'] = currentUser();
|
$existing['muokkaaja'] = currentUser();
|
||||||
dbSaveIpam($companyId, $existing);
|
dbSaveIpam($companyId, $existing);
|
||||||
@@ -1991,7 +1956,7 @@ switch ($action) {
|
|||||||
'nimi' => $device['nimi'],
|
'nimi' => $device['nimi'],
|
||||||
'verkko' => $cleanIp,
|
'verkko' => $cleanIp,
|
||||||
'vlan_id' => null,
|
'vlan_id' => null,
|
||||||
'site_id' => $device['site_id'],
|
'site_id' => $device['laitetila_id'],
|
||||||
'tila' => 'varattu',
|
'tila' => 'varattu',
|
||||||
'asiakas' => '',
|
'asiakas' => '',
|
||||||
'lisatiedot' => 'Automaattinen varaus laitteelta: ' . $device['nimi'],
|
'lisatiedot' => 'Automaattinen varaus laitteelta: ' . $device['nimi'],
|
||||||
|
|||||||
50
db.php
50
db.php
@@ -639,6 +639,20 @@ function initDatabase(): void {
|
|||||||
try {
|
try {
|
||||||
$db->query("UPDATE users SET role = 'user' WHERE role = 'admin'");
|
$db->query("UPDATE users SET role = 'user' WHERE role = 'admin'");
|
||||||
} catch (\Throwable $e) { /* ohitetaan */ }
|
} catch (\Throwable $e) { /* ohitetaan */ }
|
||||||
|
|
||||||
|
// Migraatio: yhdistä sites → laitetilat (kopioi vanhat sijainnit laitetiloiksi)
|
||||||
|
try {
|
||||||
|
// Kopioi sites-taulun rivit laitetilat-tauluun (ohita duplikaatit)
|
||||||
|
$db->query("
|
||||||
|
INSERT IGNORE INTO laitetilat (id, company_id, nimi, kuvaus, osoite, luotu, muokattu, muokkaaja)
|
||||||
|
SELECT id, company_id, nimi, '', CONCAT(IFNULL(osoite,''), IF(kaupunki != '', CONCAT(', ', kaupunki), '')), NOW(), NOW(), ''
|
||||||
|
FROM sites
|
||||||
|
");
|
||||||
|
// Päivitä laitteiden laitetila_id vanhoista site_id-viittauksista
|
||||||
|
$db->query("UPDATE devices SET laitetila_id = site_id WHERE laitetila_id IS NULL AND site_id IS NOT NULL");
|
||||||
|
// Päivitä IPAM-merkintöjen site_id viittaamaan laitetiloihin (jo sama ID)
|
||||||
|
// (ei tarvitse muuttaa koska ID:t ovat samat)
|
||||||
|
} catch (\Throwable $e) { /* ohitetaan */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== YRITYKSET ====================
|
// ==================== YRITYKSET ====================
|
||||||
@@ -1040,40 +1054,16 @@ function dbDeleteCustomer(string $customerId): void {
|
|||||||
_dbExecute("DELETE FROM customers WHERE id = ?", [$customerId]);
|
_dbExecute("DELETE FROM customers WHERE id = ?", [$customerId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SIJAINNIT (SITES) ====================
|
// ==================== SIJAINNIT (SITES) — POISTETTU, KÄYTETÄÄN LAITETILOJA ====================
|
||||||
|
// Sites on yhdistetty laitetiloihin. Migraatio kopioi vanhat sites → laitetilat.
|
||||||
function dbLoadSites(string $companyId): array {
|
// dbLoadSites, dbSaveSite, dbDeleteSite poistettu.
|
||||||
return _dbFetchAll("SELECT * FROM sites WHERE company_id = ? ORDER BY nimi", [$companyId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dbSaveSite(string $companyId, array $site): void {
|
|
||||||
_dbExecute("
|
|
||||||
INSERT INTO sites (id, company_id, nimi, osoite, kaupunki)
|
|
||||||
VALUES (:id, :company_id, :nimi, :osoite, :kaupunki)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
nimi = VALUES(nimi), osoite = VALUES(osoite), kaupunki = VALUES(kaupunki)
|
|
||||||
", [
|
|
||||||
'id' => $site['id'],
|
|
||||||
'company_id' => $companyId,
|
|
||||||
'nimi' => $site['nimi'] ?? '',
|
|
||||||
'osoite' => $site['osoite'] ?? '',
|
|
||||||
'kaupunki' => $site['kaupunki'] ?? '',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dbDeleteSite(string $siteId): void {
|
|
||||||
// Nollaa viittaavien laitteiden site_id
|
|
||||||
_dbExecute("UPDATE devices SET site_id = NULL WHERE site_id = ?", [$siteId]);
|
|
||||||
_dbExecute("DELETE FROM sites WHERE id = ?", [$siteId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== LAITTEET (DEVICES) ====================
|
// ==================== LAITTEET (DEVICES) ====================
|
||||||
|
|
||||||
function dbLoadDevices(string $companyId): array {
|
function dbLoadDevices(string $companyId): array {
|
||||||
$devices = _dbFetchAll("
|
$devices = _dbFetchAll("
|
||||||
SELECT d.*, s.nimi AS site_name, lt.nimi AS laitetila_name
|
SELECT d.*, lt.nimi AS laitetila_name
|
||||||
FROM devices d
|
FROM devices d
|
||||||
LEFT JOIN sites s ON d.site_id = s.id
|
|
||||||
LEFT JOIN laitetilat lt ON d.laitetila_id = lt.id
|
LEFT JOIN laitetilat lt ON d.laitetila_id = lt.id
|
||||||
WHERE d.company_id = ?
|
WHERE d.company_id = ?
|
||||||
ORDER BY d.nimi
|
ORDER BY d.nimi
|
||||||
@@ -1121,9 +1111,9 @@ function dbDeleteDevice(string $deviceId): void {
|
|||||||
|
|
||||||
function dbLoadIpam(string $companyId): array {
|
function dbLoadIpam(string $companyId): array {
|
||||||
$rows = _dbFetchAll("
|
$rows = _dbFetchAll("
|
||||||
SELECT i.*, s.nimi AS site_name
|
SELECT i.*, lt.nimi AS site_name
|
||||||
FROM ipam i
|
FROM ipam i
|
||||||
LEFT JOIN sites s ON i.site_id = s.id
|
LEFT JOIN laitetilat lt ON i.site_id = lt.id
|
||||||
WHERE i.company_id = ?
|
WHERE i.company_id = ?
|
||||||
ORDER BY i.tyyppi, i.vlan_id, i.verkko
|
ORDER BY i.tyyppi, i.vlan_id, i.verkko
|
||||||
", [$companyId]);
|
", [$companyId]);
|
||||||
|
|||||||
42
index.html
42
index.html
@@ -203,7 +203,6 @@
|
|||||||
<div class="tab-content" id="tab-content-tekniikka">
|
<div class="tab-content" id="tab-content-tekniikka">
|
||||||
<div class="sub-tab-bar">
|
<div class="sub-tab-bar">
|
||||||
<button class="sub-tab active" data-subtab="devices">Laitteet</button>
|
<button class="sub-tab active" data-subtab="devices">Laitteet</button>
|
||||||
<button class="sub-tab" data-subtab="sites">Sijainnit</button>
|
|
||||||
<button class="sub-tab" data-subtab="ipam">IPAM</button>
|
<button class="sub-tab" data-subtab="ipam">IPAM</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -241,35 +240,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sub-tab: Sijainnit -->
|
|
||||||
<div class="sub-tab-content" id="subtab-sites">
|
|
||||||
<div class="main-container">
|
|
||||||
<div class="search-bar" style="display:flex;gap:0.5rem;align-items:center;">
|
|
||||||
<input type="text" id="site-search-input" placeholder="Hae sijainteja..." style="flex:1;">
|
|
||||||
</div>
|
|
||||||
<div class="table-card">
|
|
||||||
<table id="site-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Nimi</th>
|
|
||||||
<th>Osoite</th>
|
|
||||||
<th>Kaupunki</th>
|
|
||||||
<th>Laitteita</th>
|
|
||||||
<th>Toiminnot</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="site-tbody"></tbody>
|
|
||||||
</table>
|
|
||||||
<div id="no-sites-tab" class="empty-state" style="display:none;">
|
|
||||||
<p>Ei sijainteja vielä. Lisää ensimmäinen sijainti.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-bar">
|
|
||||||
<span id="site-count">0 sijaintia</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Sub-tab: IPAM -->
|
<!-- Sub-tab: IPAM -->
|
||||||
<div class="sub-tab-content" id="subtab-ipam">
|
<div class="sub-tab-content" id="subtab-ipam">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
@@ -1763,15 +1733,9 @@
|
|||||||
<input type="text" id="device-form-serial">
|
<input type="text" id="device-form-serial">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="device-form-site">Sijainti</label>
|
<label for="device-form-laitetila">Sijainti / Laitetila</label>
|
||||||
<select id="device-form-site">
|
|
||||||
<option value="">— Ei sijaintia —</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="device-form-laitetila">Laitetila</label>
|
|
||||||
<select id="device-form-laitetila">
|
<select id="device-form-laitetila">
|
||||||
<option value="">— Ei laitetilaa —</option>
|
<option value="">— Ei sijaintia —</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -1860,7 +1824,7 @@
|
|||||||
<input type="text" id="ipam-form-nimi" placeholder="esim. Asiakasverkko">
|
<input type="text" id="ipam-form-nimi" placeholder="esim. Asiakasverkko">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ipam-form-site">Sijainti</label>
|
<label for="ipam-form-site">Sijainti / Laitetila</label>
|
||||||
<select id="ipam-form-site">
|
<select id="ipam-form-site">
|
||||||
<option value="">— Ei sijaintia —</option>
|
<option value="">— Ei sijaintia —</option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
117
script.js
117
script.js
@@ -274,9 +274,9 @@ function switchToTab(target, subTab) {
|
|||||||
if (target === 'customers') loadCustomers();
|
if (target === 'customers') loadCustomers();
|
||||||
if (target === 'leads') loadLeads();
|
if (target === 'leads') loadLeads();
|
||||||
if (target === 'tekniikka') {
|
if (target === 'tekniikka') {
|
||||||
loadDevices(); loadSitesTab(); loadIpam();
|
loadDevices(); loadIpam();
|
||||||
// Palauta sub-tab
|
// Palauta sub-tab
|
||||||
const validSubTabs = ['devices', 'sites', 'ipam'];
|
const validSubTabs = ['devices', 'ipam'];
|
||||||
if (subTab && validSubTabs.includes(subTab)) switchSubTab(subTab);
|
if (subTab && validSubTabs.includes(subTab)) switchSubTab(subTab);
|
||||||
}
|
}
|
||||||
if (target === 'archive') loadArchive();
|
if (target === 'archive') loadArchive();
|
||||||
@@ -2493,8 +2493,6 @@ async function showCompanyDetail(id) {
|
|||||||
|
|
||||||
// Lataa postilaatikot
|
// Lataa postilaatikot
|
||||||
loadMailboxes();
|
loadMailboxes();
|
||||||
// Lataa sijainnit
|
|
||||||
loadSites();
|
|
||||||
// Lataa käyttäjäoikeudet
|
// Lataa käyttäjäoikeudet
|
||||||
loadCompanyUsers(id);
|
loadCompanyUsers(id);
|
||||||
}
|
}
|
||||||
@@ -2848,7 +2846,6 @@ async function toggleCompanyUser(userId, companyId, add) {
|
|||||||
// ==================== LAITTEET (DEVICES) ====================
|
// ==================== LAITTEET (DEVICES) ====================
|
||||||
|
|
||||||
let devicesData = [];
|
let devicesData = [];
|
||||||
let sitesData = [];
|
|
||||||
|
|
||||||
async function loadDevices() {
|
async function loadDevices() {
|
||||||
try {
|
try {
|
||||||
@@ -2865,7 +2862,7 @@ function renderDevices() {
|
|||||||
(d.nimi || '').toLowerCase().includes(query) ||
|
(d.nimi || '').toLowerCase().includes(query) ||
|
||||||
(d.hallintaosoite || '').toLowerCase().includes(query) ||
|
(d.hallintaosoite || '').toLowerCase().includes(query) ||
|
||||||
(d.serial || '').toLowerCase().includes(query) ||
|
(d.serial || '').toLowerCase().includes(query) ||
|
||||||
(d.site_name || '').toLowerCase().includes(query) ||
|
(d.laitetila_name || '').toLowerCase().includes(query) ||
|
||||||
(d.funktio || '').toLowerCase().includes(query) ||
|
(d.funktio || '').toLowerCase().includes(query) ||
|
||||||
(d.tyyppi || '').toLowerCase().includes(query) ||
|
(d.tyyppi || '').toLowerCase().includes(query) ||
|
||||||
(d.malli || '').toLowerCase().includes(query)
|
(d.malli || '').toLowerCase().includes(query)
|
||||||
@@ -2884,7 +2881,7 @@ function renderDevices() {
|
|||||||
<td><strong>${esc(d.nimi)}</strong></td>
|
<td><strong>${esc(d.nimi)}</strong></td>
|
||||||
<td><code style="font-size:0.82rem;">${esc(d.hallintaosoite || '-')}</code></td>
|
<td><code style="font-size:0.82rem;">${esc(d.hallintaosoite || '-')}</code></td>
|
||||||
<td style="font-size:0.85rem;">${esc(d.serial || '-')}</td>
|
<td style="font-size:0.85rem;">${esc(d.serial || '-')}</td>
|
||||||
<td>${d.site_name ? esc(d.site_name) : '<span style="color:#ccc;">-</span>'}</td>
|
<td>${d.laitetila_name ? esc(d.laitetila_name) : '<span style="color:#ccc;">-</span>'}</td>
|
||||||
<td>${esc(d.tyyppi || '-')}</td>
|
<td>${esc(d.tyyppi || '-')}</td>
|
||||||
<td>${esc(d.funktio || '-')}</td>
|
<td>${esc(d.funktio || '-')}</td>
|
||||||
<td>${esc(d.malli || '-')}</td>
|
<td>${esc(d.malli || '-')}</td>
|
||||||
@@ -2924,8 +2921,7 @@ async function editDevice(id) {
|
|||||||
document.getElementById('device-form-malli').value = d.malli || '';
|
document.getElementById('device-form-malli').value = d.malli || '';
|
||||||
document.getElementById('device-form-ping-check').checked = d.ping_check || false;
|
document.getElementById('device-form-ping-check').checked = d.ping_check || false;
|
||||||
document.getElementById('device-form-lisatiedot').value = d.lisatiedot || '';
|
document.getElementById('device-form-lisatiedot').value = d.lisatiedot || '';
|
||||||
await loadSitesAndLaitetilatForDropdown();
|
await loadLaitetilatForDropdown();
|
||||||
document.getElementById('device-form-site').value = d.site_id || '';
|
|
||||||
document.getElementById('device-form-laitetila').value = d.laitetila_id || '';
|
document.getElementById('device-form-laitetila').value = d.laitetila_id || '';
|
||||||
document.getElementById('device-modal-title').textContent = 'Muokkaa laitetta';
|
document.getElementById('device-modal-title').textContent = 'Muokkaa laitetta';
|
||||||
document.getElementById('device-modal').style.display = 'flex';
|
document.getElementById('device-modal').style.display = 'flex';
|
||||||
@@ -2939,17 +2935,14 @@ async function deleteDevice(id, name) {
|
|||||||
} catch (e) { alert(e.message); }
|
} catch (e) { alert(e.message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSitesForDropdown() { await loadSitesAndLaitetilatForDropdown(); }
|
async function loadSitesForDropdown() { await loadLaitetilatForDropdown(); }
|
||||||
|
async function loadSitesAndLaitetilatForDropdown() { await loadLaitetilatForDropdown(); }
|
||||||
|
|
||||||
async function loadSitesAndLaitetilatForDropdown() {
|
async function loadLaitetilatForDropdown() {
|
||||||
try {
|
try {
|
||||||
const [sites, tilat] = await Promise.all([apiCall('sites'), apiCall('laitetilat')]);
|
const tilat = await apiCall('laitetilat');
|
||||||
sitesData = sites;
|
|
||||||
const siteSel = document.getElementById('device-form-site');
|
|
||||||
siteSel.innerHTML = '<option value="">— Ei sijaintia —</option>' +
|
|
||||||
sitesData.map(s => `<option value="${s.id}">${esc(s.nimi)}${s.kaupunki ? ' (' + esc(s.kaupunki) + ')' : ''}</option>`).join('');
|
|
||||||
const tilaSel = document.getElementById('device-form-laitetila');
|
const tilaSel = document.getElementById('device-form-laitetila');
|
||||||
tilaSel.innerHTML = '<option value="">— Ei laitetilaa —</option>' +
|
tilaSel.innerHTML = '<option value="">— Ei sijaintia —</option>' +
|
||||||
tilat.map(t => `<option value="${t.id}">${esc(t.nimi)}${t.osoite ? ' (' + esc(t.osoite) + ')' : ''}</option>`).join('');
|
tilat.map(t => `<option value="${t.id}">${esc(t.nimi)}${t.osoite ? ' (' + esc(t.osoite) + ')' : ''}</option>`).join('');
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
@@ -2957,7 +2950,7 @@ async function loadSitesAndLaitetilatForDropdown() {
|
|||||||
document.getElementById('btn-add-device')?.addEventListener('click', async () => {
|
document.getElementById('btn-add-device')?.addEventListener('click', async () => {
|
||||||
document.getElementById('device-form-id').value = '';
|
document.getElementById('device-form-id').value = '';
|
||||||
document.getElementById('device-form').reset();
|
document.getElementById('device-form').reset();
|
||||||
await loadSitesAndLaitetilatForDropdown();
|
await loadLaitetilatForDropdown();
|
||||||
document.getElementById('device-modal-title').textContent = 'Lisää laite';
|
document.getElementById('device-modal-title').textContent = 'Lisää laite';
|
||||||
document.getElementById('device-modal').style.display = 'flex';
|
document.getElementById('device-modal').style.display = 'flex';
|
||||||
});
|
});
|
||||||
@@ -2976,7 +2969,6 @@ document.getElementById('device-form')?.addEventListener('submit', async (e) =>
|
|||||||
nimi: document.getElementById('device-form-nimi').value.trim(),
|
nimi: document.getElementById('device-form-nimi').value.trim(),
|
||||||
hallintaosoite: document.getElementById('device-form-hallintaosoite').value.trim(),
|
hallintaosoite: document.getElementById('device-form-hallintaosoite').value.trim(),
|
||||||
serial: document.getElementById('device-form-serial').value.trim(),
|
serial: document.getElementById('device-form-serial').value.trim(),
|
||||||
site_id: document.getElementById('device-form-site').value || null,
|
|
||||||
laitetila_id: document.getElementById('device-form-laitetila').value || null,
|
laitetila_id: document.getElementById('device-form-laitetila').value || null,
|
||||||
funktio: document.getElementById('device-form-funktio').value.trim(),
|
funktio: document.getElementById('device-form-funktio').value.trim(),
|
||||||
tyyppi: document.getElementById('device-form-tyyppi').value.trim(),
|
tyyppi: document.getElementById('device-form-tyyppi').value.trim(),
|
||||||
@@ -3011,86 +3003,8 @@ document.querySelectorAll('#tab-content-tekniikka .sub-tab').forEach(btn => {
|
|||||||
btn.addEventListener('click', () => switchSubTab(btn.dataset.subtab));
|
btn.addEventListener('click', () => switchSubTab(btn.dataset.subtab));
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== SIJAINNIT (SITES) — TEKNIIKKA TAB ====================
|
// ==================== SIJAINNIT — YHDISTETTY LAITETILOIHIN ====================
|
||||||
|
// Sites-koodi poistettu: sijainnit hallitaan nyt Laitetilat-välilehdellä.
|
||||||
let sitesTabData = [];
|
|
||||||
|
|
||||||
async function loadSitesTab() {
|
|
||||||
try {
|
|
||||||
sitesData = await apiCall('sites');
|
|
||||||
sitesTabData = sitesData;
|
|
||||||
renderSitesTab();
|
|
||||||
} catch (e) { console.error(e); }
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSitesTab() {
|
|
||||||
const query = (document.getElementById('site-search-input')?.value || '').toLowerCase().trim();
|
|
||||||
let filtered = sitesTabData;
|
|
||||||
if (query) {
|
|
||||||
filtered = sitesTabData.filter(s =>
|
|
||||||
(s.nimi || '').toLowerCase().includes(query) ||
|
|
||||||
(s.osoite || '').toLowerCase().includes(query) ||
|
|
||||||
(s.kaupunki || '').toLowerCase().includes(query)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const tbody = document.getElementById('site-tbody');
|
|
||||||
const noSites = document.getElementById('no-sites-tab');
|
|
||||||
if (filtered.length === 0) {
|
|
||||||
tbody.innerHTML = '';
|
|
||||||
if (noSites) noSites.style.display = 'block';
|
|
||||||
} else {
|
|
||||||
if (noSites) noSites.style.display = 'none';
|
|
||||||
tbody.innerHTML = filtered.map(s => {
|
|
||||||
const deviceCount = devicesData.filter(d => d.site_id === s.id).length;
|
|
||||||
return `<tr>
|
|
||||||
<td><strong>${esc(s.nimi)}</strong></td>
|
|
||||||
<td>${esc(s.osoite || '-')}</td>
|
|
||||||
<td>${esc(s.kaupunki || '-')}</td>
|
|
||||||
<td style="text-align:center;">${deviceCount}</td>
|
|
||||||
<td class="actions-cell">
|
|
||||||
<button class="btn-link" onclick="editSiteTab('${s.id}')">✎</button>
|
|
||||||
<button class="btn-link" style="color:#dc2626;" onclick="deleteSite('${s.id}','${esc(s.nimi)}')">🗑</button>
|
|
||||||
</td>
|
|
||||||
</tr>`;
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
document.getElementById('site-count').textContent = filtered.length + ' sijaintia';
|
|
||||||
}
|
|
||||||
|
|
||||||
function editSiteTab(id) {
|
|
||||||
const s = sitesData.find(x => x.id === id);
|
|
||||||
if (!s) return;
|
|
||||||
document.getElementById('site-form-id').value = s.id;
|
|
||||||
document.getElementById('site-form-nimi').value = s.nimi || '';
|
|
||||||
document.getElementById('site-form-osoite').value = s.osoite || '';
|
|
||||||
document.getElementById('site-form-kaupunki').value = s.kaupunki || '';
|
|
||||||
document.getElementById('site-form-title').textContent = 'Muokkaa sijaintia';
|
|
||||||
document.getElementById('site-form-container').style.display = '';
|
|
||||||
// Varmista että asetukset-tab ja yrityksen tiedot näkyvissä, scrollaa lomakkeeseen
|
|
||||||
switchToTab('settings');
|
|
||||||
document.getElementById('company-detail-view').style.display = '';
|
|
||||||
document.getElementById('companies-list-view').style.display = 'none';
|
|
||||||
setTimeout(() => document.getElementById('site-form-container')?.scrollIntoView({ behavior: 'smooth', block: 'center' }), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alias vanhalle editSite-funktiolle
|
|
||||||
function editSite(id) { editSiteTab(id); }
|
|
||||||
|
|
||||||
async function deleteSite(id, name) {
|
|
||||||
if (!confirm(`Poistetaanko sijainti "${name}"? Laitteet joissa tämä sijainti on menettävät sijainti-viittauksen.`)) return;
|
|
||||||
try {
|
|
||||||
await apiCall('site_delete', 'POST', { id });
|
|
||||||
loadSitesTab();
|
|
||||||
loadDevices();
|
|
||||||
} catch (e) { alert(e.message); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alias loadSites (Tekniikka sub-tab kutsuun)
|
|
||||||
async function loadSites() { await loadSitesTab(); }
|
|
||||||
|
|
||||||
function renderSites() {}
|
|
||||||
|
|
||||||
document.getElementById('site-search-input')?.addEventListener('input', () => renderSitesTab());
|
|
||||||
|
|
||||||
// ==================== IPAM ====================
|
// ==================== IPAM ====================
|
||||||
|
|
||||||
@@ -3521,12 +3435,13 @@ async function ipamAddFromFree(verkko) {
|
|||||||
document.getElementById('ipam-form-nimi')?.focus();
|
document.getElementById('ipam-form-nimi')?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ipamLaitetilatCache = null;
|
||||||
async function loadIpamSitesDropdown() {
|
async function loadIpamSitesDropdown() {
|
||||||
try {
|
try {
|
||||||
if (!sitesData || sitesData.length === 0) sitesData = await apiCall('sites');
|
if (!_ipamLaitetilatCache || _ipamLaitetilatCache.length === 0) _ipamLaitetilatCache = await apiCall('laitetilat');
|
||||||
const sel = document.getElementById('ipam-form-site');
|
const sel = document.getElementById('ipam-form-site');
|
||||||
sel.innerHTML = '<option value="">— Ei sijaintia —</option>' +
|
sel.innerHTML = '<option value="">— Ei sijaintia —</option>' +
|
||||||
sitesData.map(s => `<option value="${s.id}">${esc(s.nimi)}${s.kaupunki ? ' (' + esc(s.kaupunki) + ')' : ''}</option>`).join('');
|
_ipamLaitetilatCache.map(t => `<option value="${t.id}">${esc(t.nimi)}${t.osoite ? ' (' + esc(t.osoite) + ')' : ''}</option>`).join('');
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user