feat: Tekniikka-moduuli sub-tabeilla (Laitteet + Sijainnit + IPAM)

- Laitteet-tabi → Tekniikka (sub-tabit: Laitteet, Sijainnit, IPAM)
- Sijainnit siirretty omaksi taulukkonäkymäksi (+ "Lisää sijainti" laitteiden yhteydessä)
- Uusi IPAM-näkymä: IP-osoitteet, subnetit ja VLANit hallintaan
- IPAM: tyyppi (subnet/vlan/ip), verkko, VLAN-nro, sijainti, tila, asiakas
- Sub-tab-tyylit ja logiikka
- Yhteensopivuus: vanha 'devices' moduuli → 'tekniikka'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 20:18:56 +02:00
parent e37da2b40d
commit 9140c912cd
5 changed files with 558 additions and 56 deletions

View File

@@ -79,7 +79,7 @@
<button class="tab" data-tab="support">Asiakaspalvelu</button>
<button class="tab active" data-tab="customers">Asiakkaat</button>
<button class="tab" data-tab="leads">Liidit</button>
<button class="tab" data-tab="devices">Laitteet</button>
<button class="tab" data-tab="tekniikka">Tekniikka</button>
<button class="tab" data-tab="archive">Arkisto</button>
<button class="tab" data-tab="changelog">Muutosloki</button>
<button class="tab" data-tab="settings" id="tab-settings" style="display:none">API</button>
@@ -194,35 +194,108 @@
</div>
<!-- Tab: Laitteet -->
<div class="tab-content" id="tab-content-devices">
<div class="main-container">
<div class="search-bar" style="display:flex;gap:0.5rem;align-items:center;">
<input type="text" id="device-search-input" placeholder="Hae laitteita..." style="flex:1;">
<button class="btn-primary" id="btn-add-device" style="white-space:nowrap;">+ Lisää laite</button>
</div>
<div class="table-card">
<table id="device-table">
<thead>
<tr>
<th data-sort="nimi">Nimi &#8597;</th>
<th>Hallintaosoite</th>
<th>Serial</th>
<th>Sijainti</th>
<th>Funktio</th>
<th>Tyyppi</th>
<th>Malli</th>
<th>Ping</th>
<th>Toiminnot</th>
</tr>
</thead>
<tbody id="device-tbody"></tbody>
</table>
<div id="no-devices" class="empty-state" style="display:none;">
<p>Ei laitteita vielä. Lisää ensimmäinen laite.</p>
<div class="tab-content" id="tab-content-tekniikka">
<div class="sub-tab-bar">
<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>
</div>
<!-- Sub-tab: Laitteet -->
<div class="sub-tab-content active" id="subtab-devices">
<div class="main-container">
<div class="search-bar" style="display:flex;gap:0.5rem;align-items:center;">
<input type="text" id="device-search-input" placeholder="Hae laitteita..." style="flex:1;">
<button class="btn-primary" id="btn-add-device" style="white-space:nowrap;">+ Lisää laite</button>
<button class="btn-secondary" id="btn-add-site-quick" style="white-space:nowrap;">+ Lisää sijainti</button>
</div>
<div class="table-card">
<table id="device-table">
<thead>
<tr>
<th data-sort="nimi">Nimi &#8597;</th>
<th>Hallintaosoite</th>
<th>Serial</th>
<th>Sijainti</th>
<th>Funktio</th>
<th>Tyyppi</th>
<th>Malli</th>
<th>Ping</th>
<th>Toiminnot</th>
</tr>
</thead>
<tbody id="device-tbody"></tbody>
</table>
<div id="no-devices" class="empty-state" style="display:none;">
<p>Ei laitteita vielä. Lisää ensimmäinen laite.</p>
</div>
</div>
<div class="summary-bar">
<span id="device-count">0 laitetta</span>
</div>
</div>
<div class="summary-bar">
<span id="device-count">0 laitetta</span>
</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;">
<button class="btn-primary" id="btn-add-site-tab" style="white-space:nowrap;">+ Lisää sijainti</button>
</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 -->
<div class="sub-tab-content" id="subtab-ipam">
<div class="main-container">
<div class="search-bar" style="display:flex;gap:0.5rem;align-items:center;">
<input type="text" id="ipam-search-input" placeholder="Hae IP, VLAN, verkko..." style="flex:1;">
<button class="btn-primary" id="btn-add-ipam" style="white-space:nowrap;">+ Lisää merkintä</button>
</div>
<div class="table-card">
<table id="ipam-table">
<thead>
<tr>
<th>Tyyppi</th>
<th>Verkko / IP</th>
<th>VLAN</th>
<th>Nimi / Kuvaus</th>
<th>Sijainti</th>
<th>Tila</th>
<th>Asiakas</th>
<th>Toiminnot</th>
</tr>
</thead>
<tbody id="ipam-tbody"></tbody>
</table>
<div id="no-ipam" class="empty-state" style="display:none;">
<p>Ei IPAM-merkintöjä vielä.</p>
</div>
</div>
<div class="summary-bar">
<span id="ipam-count">0 merkintää</span>
</div>
</div>
</div>
</div>
@@ -656,7 +729,7 @@
<input type="checkbox" data-module="leads"> Liidit
</label>
<label style="display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;cursor:pointer;">
<input type="checkbox" data-module="devices"> Laitteet
<input type="checkbox" data-module="tekniikka"> Tekniikka
</label>
<label style="display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;cursor:pointer;">
<input type="checkbox" data-module="archive" checked> Arkisto
@@ -834,6 +907,67 @@
</div>
</div>
<!-- IPAM Modal -->
<div id="ipam-modal" class="modal" style="display:none">
<div class="modal-content" style="max-width:560px;">
<div class="modal-header">
<h2 id="ipam-modal-title">Lisää IPAM-merkintä</h2>
<button class="modal-close" id="ipam-modal-close">&times;</button>
</div>
<form id="ipam-form">
<input type="hidden" id="ipam-form-id">
<div class="form-grid">
<div class="form-group">
<label for="ipam-form-tyyppi">Tyyppi *</label>
<select id="ipam-form-tyyppi">
<option value="subnet">Subnet</option>
<option value="vlan">VLAN</option>
<option value="ip">IP-osoite</option>
</select>
</div>
<div class="form-group">
<label for="ipam-form-verkko">Verkko / IP</label>
<input type="text" id="ipam-form-verkko" placeholder="esim. 10.0.0.0/24 tai 192.168.1.5">
</div>
<div class="form-group">
<label for="ipam-form-vlan">VLAN-numero</label>
<input type="number" id="ipam-form-vlan" min="1" max="4094" placeholder="esim. 100">
</div>
<div class="form-group">
<label for="ipam-form-nimi">Nimi / Kuvaus</label>
<input type="text" id="ipam-form-nimi" placeholder="esim. Asiakasverkko">
</div>
<div class="form-group">
<label for="ipam-form-site">Sijainti</label>
<select id="ipam-form-site">
<option value="">— Ei sijaintia —</option>
</select>
</div>
<div class="form-group">
<label for="ipam-form-tila">Tila</label>
<select id="ipam-form-tila">
<option value="vapaa">Vapaa</option>
<option value="varattu">Varattu</option>
<option value="reserved">Reserved</option>
</select>
</div>
<div class="form-group">
<label for="ipam-form-asiakas">Asiakas</label>
<input type="text" id="ipam-form-asiakas" placeholder="Kenelle varattu">
</div>
<div class="form-group full-width">
<label for="ipam-form-lisatiedot">Lisätiedot</label>
<textarea id="ipam-form-lisatiedot" rows="2"></textarea>
</div>
</div>
<div class="form-actions" style="display:flex;gap:0.5rem;margin-top:1rem;">
<button type="submit" class="btn-primary">Tallenna</button>
<button type="button" class="btn-secondary" id="ipam-form-cancel">Peruuta</button>
</div>
</form>
</div>
</div>
<div id="customer-modal" class="modal" style="display:none">
<div class="modal-content modal-wide">
<div class="modal-header">