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:
117
script.js
117
script.js
@@ -274,9 +274,9 @@ function switchToTab(target, subTab) {
|
||||
if (target === 'customers') loadCustomers();
|
||||
if (target === 'leads') loadLeads();
|
||||
if (target === 'tekniikka') {
|
||||
loadDevices(); loadSitesTab(); loadIpam();
|
||||
loadDevices(); loadIpam();
|
||||
// Palauta sub-tab
|
||||
const validSubTabs = ['devices', 'sites', 'ipam'];
|
||||
const validSubTabs = ['devices', 'ipam'];
|
||||
if (subTab && validSubTabs.includes(subTab)) switchSubTab(subTab);
|
||||
}
|
||||
if (target === 'archive') loadArchive();
|
||||
@@ -2493,8 +2493,6 @@ async function showCompanyDetail(id) {
|
||||
|
||||
// Lataa postilaatikot
|
||||
loadMailboxes();
|
||||
// Lataa sijainnit
|
||||
loadSites();
|
||||
// Lataa käyttäjäoikeudet
|
||||
loadCompanyUsers(id);
|
||||
}
|
||||
@@ -2848,7 +2846,6 @@ async function toggleCompanyUser(userId, companyId, add) {
|
||||
// ==================== LAITTEET (DEVICES) ====================
|
||||
|
||||
let devicesData = [];
|
||||
let sitesData = [];
|
||||
|
||||
async function loadDevices() {
|
||||
try {
|
||||
@@ -2865,7 +2862,7 @@ function renderDevices() {
|
||||
(d.nimi || '').toLowerCase().includes(query) ||
|
||||
(d.hallintaosoite || '').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.tyyppi || '').toLowerCase().includes(query) ||
|
||||
(d.malli || '').toLowerCase().includes(query)
|
||||
@@ -2884,7 +2881,7 @@ function renderDevices() {
|
||||
<td><strong>${esc(d.nimi)}</strong></td>
|
||||
<td><code style="font-size:0.82rem;">${esc(d.hallintaosoite || '-')}</code></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.funktio || '-')}</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-ping-check').checked = d.ping_check || false;
|
||||
document.getElementById('device-form-lisatiedot').value = d.lisatiedot || '';
|
||||
await loadSitesAndLaitetilatForDropdown();
|
||||
document.getElementById('device-form-site').value = d.site_id || '';
|
||||
await loadLaitetilatForDropdown();
|
||||
document.getElementById('device-form-laitetila').value = d.laitetila_id || '';
|
||||
document.getElementById('device-modal-title').textContent = 'Muokkaa laitetta';
|
||||
document.getElementById('device-modal').style.display = 'flex';
|
||||
@@ -2939,17 +2935,14 @@ async function deleteDevice(id, name) {
|
||||
} 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 {
|
||||
const [sites, tilat] = await Promise.all([apiCall('sites'), 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 tilat = await apiCall('laitetilat');
|
||||
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('');
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
@@ -2957,7 +2950,7 @@ async function loadSitesAndLaitetilatForDropdown() {
|
||||
document.getElementById('btn-add-device')?.addEventListener('click', async () => {
|
||||
document.getElementById('device-form-id').value = '';
|
||||
document.getElementById('device-form').reset();
|
||||
await loadSitesAndLaitetilatForDropdown();
|
||||
await loadLaitetilatForDropdown();
|
||||
document.getElementById('device-modal-title').textContent = 'Lisää laite';
|
||||
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(),
|
||||
hallintaosoite: document.getElementById('device-form-hallintaosoite').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,
|
||||
funktio: document.getElementById('device-form-funktio').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));
|
||||
});
|
||||
|
||||
// ==================== SIJAINNIT (SITES) — TEKNIIKKA TAB ====================
|
||||
|
||||
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());
|
||||
// ==================== SIJAINNIT — YHDISTETTY LAITETILOIHIN ====================
|
||||
// Sites-koodi poistettu: sijainnit hallitaan nyt Laitetilat-välilehdellä.
|
||||
|
||||
// ==================== IPAM ====================
|
||||
|
||||
@@ -3521,12 +3435,13 @@ async function ipamAddFromFree(verkko) {
|
||||
document.getElementById('ipam-form-nimi')?.focus();
|
||||
}
|
||||
|
||||
let _ipamLaitetilatCache = null;
|
||||
async function loadIpamSitesDropdown() {
|
||||
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');
|
||||
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); }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user