Commit Graph

140 Commits

Author SHA1 Message Date
20a2b78782 Make Telegram chat_id per-company, bot token stays global
Each company can now have its own Telegram channel/group for alerts.
- Bot token: global (superadmin only, shared across companies)
- Chat ID: per-company (stored in integrations table config)
- sendTelegramAlert reads chat_id from company integration
- Test message shows company name
- Non-superadmin users can't see/edit bot token

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:05:57 +02:00
bbfff2f8b5 Mobile responsiveness + IP allow list comments
Mobile improvements:
- Fix overflow issues with global max-width/overflow-x hidden
- Stack ticket detail selects on mobile instead of one long row
- Compact header: hide less important buttons, scrollable right side
- Stat cards in 2-column grid on mobile
- Force flex-wrap on inline-styled flex containers
- Hide subtitle and user-info on small phones
- Ticket selects full-width on small phones

IP allow list:
- Support # comments after IP addresses (e.g. "192.168.1.1 # Office VPN")
- Updated placeholder with comment examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:49:07 +02:00
45de863d07 Security: defense-in-depth company isolation for all operations
Critical fixes:
- company_logo_upload: validate user has access to target company
- All delete functions (db.php): accept optional company_id parameter
  for defense-in-depth filtering (customers, devices, ipam, guides,
  leads, tickets, archives, mailboxes, rules, templates, todos)
- All API delete calls now pass company_id to db layer
- ticket_bulk_delete: per-ticket company_id filtering
- todo_comment/time/subtask operations: verify todo belongs to company
- dbGetMailbox: optional company_id scoping, used in smtp_test
- requireCompanyOrParam: no longer mutates session permanently
- Fix _dbFetch typo in zammad_attachment (was runtime error)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:27:13 +02:00
a73cce678e Sync ticket status to Zammad + prevent closed tickets reopening
- When changing ticket status in intra, now syncs to Zammad too
  (close in intra → close in Zammad, etc.)
- Prevent Zammad sync from reopening locally closed tickets
  unless Zammad has genuinely new activity (updated_at changed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:15:16 +02:00
b4a85a28a5 Group users by company + allow admins to set user/admin role
- Superadmin sees users grouped by company with header rows
- Admins can now set user or admin role when creating/editing users
- Admin role change restricted to own company only
- Prevents admin from modifying superadmin roles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:16:04 +02:00
a94d1edee0 Add SSL certificate provisioning button for superadmin
- New provision_ssl API endpoint runs certbot for new domains
- SSL button appears next to domain textarea for superadmin
- Shell script on server handles Apache config + Let's Encrypt
- DNS check skips domains without resolution to avoid certbot errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:09:34 +02:00
d4e06fd586 Optimize Zammad sync: skip closed tickets in incremental sync
- Incremental sync now excludes closed/merged/removed tickets from query
- Full sync still fetches everything
- Reopened tickets (closed→new/open in Zammad) are handled correctly:
  existing ticket is updated instead of creating duplicate

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:56:38 +02:00
15223760f5 Auto-update ticket status to käsittelyssä on first reply
- zammad_reply endpoint now sets intra status uusi→kasittelyssa
- Also updates Zammad ticket state to open
- Fixed stateMap: Zammad open now maps to kasittelyssa instead of avoin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:53:59 +02:00
6bbd224104 Poista ticket_fetch changelog-kirjaus
Sähköpostien haku ei ole muutostoiminto eikä kuulu muutoslokiin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:06:45 +02:00
a69fed75e4 Zammad sync performance + liitteiden näyttö tikettilistassa
- Rajoita artikkelien haku max 10 tikettiin per sync (loput on-demand)
- Curl timeout 15s + connect timeout 5s
- Frontend: IMAP ja Zammad haetaan rinnakkain (Promise.allSettled)
- Auto-refresh: Zammad sync ei blokkaa tikettien latausta
- Hakaneula-ikoni (📎) tikettilistassa kun viestissä on liitteitä
- has_attachments, source, zammad_group, ticket_number tikettilistaan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:56:13 +02:00
dc0ed5c75c Liitteiden näyttö Zammad-viesteissä + download proxy
- dbLoadTickets: attachments & zammad_article_id mukaan viestidataan
- ticket_detail on-demand: liitteiden metadata talteen (sama kuin sync)
- Uusi zammad_attachment proxy-endpoint liitteiden lataukseen
- JS: liitteet näkyvät tikettiviesteissä (ikoni, nimi, koko, latauslinkki)
- CSS: .msg-attachments ja .msg-attachment-link tyylit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 03:14:15 +02:00
376912b9ff Liitetiedostot tikettivastausten sähköposteihin + muistiinpanojen piilotus
- Lisää liitetiedostojen tuki vastauslomakkeeseen (📎 Liitä tiedosto -nappi)
- Tuki max 25 MB tiedostoille, useita liitteitä kerralla
- Zammad API: liitteet base64-muodossa attachments-kentässä
- SMTP: multipart/mixed MIME (boundary, Content-Disposition: attachment)
- Sisäiset muistiinpanot (note) suodatetaan pois quoted threadista
  (eivät näy asiakkaille lähtevissä sähköposteissa)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 03:00:30 +02:00
6f1d9ed5d4 BCC-kenttä tiketteihin + To/CC/BCC tallennus + allekirjoituskorjaus
- Lisää BCC-kenttä vastauslomakkeeseen (HTML, JS, API)
- To/CC/BCC tallentuvat tiketille pysyvästi (seuraava vastaus muistaa muutokset)
- Lisää to_email ja bcc sarakkeet tickets-tauluun
- BCC-tuki SMTP-lähetykseen (RCPT TO ilman headeria)
- Korjaa allekirjoitukset: buildSignaturesWithDefaults() generoi nyt oletukset
  myös Zammad-sähköposteille (support@web1.fi ym.), ei pelkille mailboxeille
- Allekirjoituksiin lisätty puhelinnumero

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:54:57 +02:00
3d66319d89 Korjaa sähköpostivastausten välilyönnit: nl2br + &nbsp; pre-wrap:n sijaan
white-space:pre-wrap ei toimi kaikissa sähköpostiohjelmissa/Zammadissa.
Vaihdettu käyttämään nl2br() rivinvaihdoille ja &nbsp; peräkkäisille
välilyönneille, jotka toimivat universaalisti HTML-sähköposteissa.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:46:28 +02:00
8d5ef864f9 Saatavuuskyselyt: IP-organisaatio, siirrä API-tabiin + sähköpostien formatointi + tikettiviestivärit
- Lisää IP-organisaatio/ISP-kenttä saatavuuskyselyihin (ip-api.com haku)
- Siirrä saatavuuskyselyt-taulukko Asiakkaat-tabista API-asetussivulle
- Korjaa rivinvaihdot ja välilyönnit Zammad-sähköpostivastauksissa (white-space:pre-wrap)
- Korjaa quoted thread: plain-text viestit muunnetaan HTML:ksi oikein
- Tikettiviestiketjun värit selkeämmiksi (sininen=saapuva, vihreä=lähtevä)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:40:32 +02:00
74380a3176 Saatavuuskyselyt: IP/hostname, duplikaattien esto
- Reverse DNS -haku tallentaa hostnamen IP:n rinnalle (paljastaa
  operaattorin ja alueen, esim. dsl-hel-123.elisa.fi)
- Duplikaattikyselyn (sama osoite+postinumero+kaupunki) ei tallenneta
  uudelleen samalle yritykselle
- IP/hostname -sarake lisätty taulukkoon

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:26:16 +02:00
64dc02f432 Korjaa saatavuuskyselyt: näytä kaikkien yritysten kyselyt
Endpoint näytti vain aktiivisen yrityksen kyselyt, mutta kyselyt
tallennetaan API-avaimen yrityksen alle. Nyt näytetään kaikkien
käyttäjän yritysten kyselyt + yrityssarake taulukkoon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:23:40 +02:00
a38c5f4808 Lisää saatavuuskyselyjen keräys ja listausnäkymä
Jokainen nettisivujen kautta tehty saatavuustarkistus tallennetaan
tietokantaan (osoite, postinumero, kaupunki, tulos, IP, referer).
Kyselyt näkyvät Asiakkaat > Saatavuuskyselyt -välilehdellä
sivutettuna taulukkona.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:19:24 +02:00
9ba239478c Lisää viestiketju (quoted thread) tikettivastausten loppuun
Sekä Zammad- että SMTP-vastaukset sisältävät nyt koko viestiketjun
vastauksessa. Zammad-vastauksissa HTML-blockquoteilla, SMTP:ssä
plain-text > -quoting-muodossa. Vain uusi viesti tallennetaan
tietokantaan, ketju näkyy vain lähetettävässä sähköpostissa.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:08:03 +02:00
4086409f99 Välitä To/CC-kentät Zammad-vastauksissa
JS ei lähettänyt käyttäjän muokkaamaa To/CC-kenttää Zammad-vastauksen
mukana — backend käytti aina alkuperäistä lähettäjää tietokannasta.
Nyt käyttäjän syöttämä To/CC välitetään API:lle ja Zammadille.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:02:11 +02:00
6e0596959b Korjaa Zammad-vastauksen sähköpostilähetys
Lisätty subtype: 'reply' Zammad-artikkelin luontiin — ilman tätä
Zammad luo artikkelin mutta ei lähetä sähköpostia vastaanottajalle.
Myös plain-text muunnetaan HTML:ksi ja subject saa Re: -etuliitteen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:51:35 +02:00
7a20345701 Lisää puhelinnumero yrityksen asetuksiin ja allekirjoituksiin
Puhelinnumero-kenttä yrityksen asetuksissa tallennetaan tietokantaan
ja näkyy automaattisesti kaikissa oletusallekirjoituksissa viimeisenä
rivinä (sekä SMTP- että Zammad-postilaatikoille).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:30:11 +02:00
890b5d932e Lisää allekirjoitukset Zammad-sähköposteille
Asetukset-sivulla näytetään nyt myös Zammad-sähköpostiosoitteet
(esim. support@web1.fi) allekirjoitusten alla. Allekirjoitus liitetään
automaattisesti Zammad-vastauksiin. Tallennetaan avaimella
"zammad:email@osoite.fi". Uusi ticket_zammad_emails API-endpoint
hakee uniikit vastaanotto-osoitteet tietokannasta.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:12:38 +02:00
02a5c08164 Zammad-tiketit: vastaus Zammad API:n kautta + to-osoite + HTML-viestit
1. Zammad-tiketeille näytetään vastaanotto-osoite (esim. support@web1.fi)
   lähettäjäkentässä eikä SMTP-postilaatikkolistaa — vastaus menee
   Zammad API:n kautta.
2. Ensimmäisen artikkelin to-osoite tallennetaan zammad_to_email kenttään
   on-demand artikkelien haussa.
3. Korjattu _dbFetchRow → _dbFetchOne zammad_reply endpointissa.
4. sanitizeHtml() renderöi viestien HTML turvallisesti.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:06:43 +02:00
109dce3f26 Nopeuta Zammad-sync: artikkelit haetaan vasta kun tiketti avataan
Sync hakee nyt vain tikettien metatiedot (status, aihe, ryhmä) —
ei enää satoja getArticles-kutsuja jokaiselle tiketille. Artikkelit
haetaan on-demand ticket_detail-endpointissa kun käyttäjä avaa
tiketin. Nopeuttaa synkkausta dramaattisesti.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:54:11 +02:00
cbcfdaa2a3 Optimoi Zammad-sync: inkrementaalinen haku oletuksena
Auto-refresh hakee nyt vain viimeisen synkkauksen jälkeen muuttuneet
tiketit (updated_at + 5min marginaali). Artikkelit haetaan vain
uusille tai muuttuneille tiketeille. "Hae postit" -nappi tekee
edelleen full syncin (full=true). Nopeuttaa autopäivitystä merkittävästi.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:39:24 +02:00
8c950bbe8f Hae postit -nappi synkkaa myös Zammadin + suljettu tiketti avautuu uutena
1. Hae postit -nappi ajaa nyt myös zammad_sync automaattisesti
   sähköpostien haun jälkeen (ohitetaan hiljaa jos Zammad ei käytössä).
2. Kun suljettuun/ratkaistuun tikettiin tulee uusi viesti,
   tiketti avautuu uudelleen "uusi"-tilaan (aiemmin "käsittelyssä").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:19:51 +02:00
b9a76d76e5 Korjaa Zammad-ryhmien näkyvyys asetuksissa — hae API:sta eikä muistista
Zammad-ryhmät eivät näkyneet postilaatikoiden näkyvyydessä koska ne
haettiin tickets-globaalista joka on tyhjä ennen tiketti-tabin avaamista.
Lisätty ticket_zammad_groups API-endpoint joka hakee uniikit ryhmät
suoraan tietokannasta.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:15:55 +02:00
056de5054d Vaihda brändäys: Noxus Intra → Noxus HUB © Empor Oy 2026
Kaikki viittaukset "Noxus Intra" vaihdettu muotoon "Noxus HUB" koko
koodikannassa. Footer ja login-sivu näyttävät nyt "Noxus HUB © Empor Oy 2026".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:49:00 +02:00
08bce71b5b Paginointi asiakaspalveluun + Zammad-ryhmät näkyvyysasetuksiin
- Paginointi: 100 tikettiä/sivu, navigointipalkki sivujen välillä
- Filtterit resetoivat sivunumeron 1:ksi
- Select All valitsee vain nykyisen sivun tiketit
- Zammad-synkronointi tallentaa source=zammad ja zammad_group
- Postilaatikoiden näkyvyysasetuksissa Zammad-ryhmät (Zammad)-merkinnällä
- Zammad-ryhmien piilotus filtteröi tiketit samalla tavalla kuin postilaatikot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:20:25 +02:00
cc6e5c2653 Noxus-logo + API-tabi piiloon ilman integraatioita
- Luotu Noxus SVG-logo (violetti heksagoni + N)
- Demo-yritys nimetty uudelleen Noxukseksi violetilla värillä
- API-tabi piilotettuna ellei yrityksellä ole integraatioita päällä
  (superadmin näkee aina)
- check_auth palauttaa has_integrations-lipun

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:14:17 +02:00
1752397161 Fix: ZammadClient lisää https:// automaattisesti URL:iin
Kun Zammad-URL syötettiin ilman protokollaa (esim. desk.web1.fi),
cURL ei osannut muodostaa oikeaa osoitetta ja synkronointi epäonnistui.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:56:37 +02:00
6a12da858f Fix: superadmin pääsy kaikkiin yrityksiin
Superadmin-käyttäjällä oli user_companies-taulussa vain yksi yritys,
joten hän ei voinut vaihtaa tai muokata muita yrityksiä (esim. intra.web1.fi).
Nyt superadmin saa kaikki yritykset sessioon loginissa ja check_auth:ssa,
ja requireCompany() + company_switch ohittavat company-tarkistuksen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:24:03 +02:00
dbf2d2b89a Korjaa integraatio-endpointit: $companyId = requireCompany() kaikissa caseissa
Kaikki integraatio/zammad-endpointit kutsuivat requireCompany() ilman
return-arvon talteenottoa, jolloin $companyId oli null ja aiheutti
Fatal error. Korjattu: integrations, integration_save, integration_test,
zammad_groups, zammad_sync, zammad_reply.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:19:35 +02:00
bfa30009a9 Integraatiot: checkboxit vain päälle/pois Yrityksissä, kaikki asetukset API-tabissa
- Yritykset-tab: integraatio-checkboxit tallentavat vain enabled-tilan
- API-tab: Zammad/Saatavuus/Telegram kortit näkyvät kun integraatio päällä
- Zammad-asetukset (URL, token, ryhmät, synkronointi) kokonaan API-tabissa
- integration_save: tyhjä config ei ylikirjoita olemassaolevia asetuksia

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:11:31 +02:00
4a46ce56f3 Zammad-asetukset API-välilehdelle, integraatio-checkboxit erilliseksi kortiksi, korjaa tyyppi puuttuu -virhe
- Integraatiot erillinen table-card yrityksen asetuksissa (vain superadmin)
- Zammad-konfiguraatio (URL, token, ryhmät, synkronointi) siirretty API-tabiin
- Saatavuus-API, Telegram ja Zammad kortit näkyvät API-tabissa kun integraatio on enabloitu
- Korjattu integration_save ja integration_test: puuttuva $input json_decode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:00:36 +02:00
648cb949ac Superadmin-rajoitus: moduulit, integraatiot ja IP-asetukset vain pääkäyttäjälle + captcha-sessiokorjaus
- Moduulit, integraatiot ja IP-rajoitukset piilotetaan yritysadminilta (vain superadmin näkee)
- Saatavuus-API ja Telegram checkboxit tallentavat tilan heti muutoksessa
- session_regenerate_id(false) estää race conditionin kirjautumisen jälkeen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:49:57 +02:00
fa8aaed11e Lisää Zammad-integraatio ja modulaarinen integraatiot-hallinta
- Uusi integrations-taulu tietokantaan (moduulimalli: type, enabled, config)
- ZammadClient-luokka: tiketit, artikkelit, vastaukset, ryhmät
- API-endpointit: integration_save, integration_test, zammad_sync, zammad_reply, zammad_groups
- Synkronointi: Zammad-tiketit → intran tiketit, artikkelit → viestit
- Vastaukset: Zammad-tiketteihin vastaus kulkee Zammad API:n kautta (→ O365)
- UI: Integraatiot-osio API-välilehdellä, toggle-kytkimet, Zammad-konfiguraatio
- tickets.zammad_ticket_id ja ticket_messages.zammad_article_id linkitys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:25:51 +02:00
1aea4bde20 Tiukempi IP-rajoitus: estä kirjautuminen ja suojaa asetukset
- Login: jos kaikki käyttäjän yritykset IP-estetty → estä
  kirjautuminen kokonaan. Valitsee automaattisesti ei-estetyn
  yrityksen aktiiviseksi.
- check_auth: jos aktiivinen yritys IP-estetty → vaihda
  sallittuun. Jos kaikki estetty → kirjaa ulos.
- company_update: vain superadmin saa muuttaa allowed_ips-kenttää.
  Estää adminia poistamasta IP-rajoitusta itseltään.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:49:24 +02:00
771d288338 Käyttäjän poisto: admin poistaa vain yrityksestä, ei kokonaan
Admin poistaa käyttäjän vain nykyisestä yrityksestä (user_companies).
Käyttäjä poistetaan kokonaan vasta kun ei kuulu enää yhteenkään
yritykseen. Superadmin poistaa edelleen kokonaan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:13:23 +02:00
df2d6d2d83 Korjaa automaattisäännöt ja viestien duplikaattiesto
- Sääntöjen kenttänimet: DB käyttää type_set/status_set mutta
  API lähetti set_type/set_status → nyt dbSaveTicketRule hyväksyy
  molemmat ja matching lukee oikeat DB-kenttänimet
- Migraatio: täytä fetched_message_ids olemassaolevien tikettien
  message_id:illä niin poistetut viestit eivät tule takaisin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:52:22 +02:00
0b04fd1d5a Korjaa ticket_fetch ja sähköpostien duplikaattiongelma
- Korjaa implode()-bugi: $email['to'] on string, ei array
- Lisää fetched_message_ids-taulu joka estää poistettujen
  tikettien uudelleenluonnin seuraavassa haussa
- Viestit haetaan postilaatikosta vain kertaalleen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:21:27 +02:00
656b5042e4 Asiakaspalvelu: alinavi-uudelleenjärjestely + tikettityyppien hallinta
Vastauspohjat, Säännöt ja Asetukset siirretty omiksi alinaveikseen
tikettilistan overlay-napeista. Säännöt-välilehdelle lisätty
tikettityyppien hallinta (lisää/poista). Tyypit tallennetaan
tietokantaan yrityskohtaisesti ja populoidaan dynaamisesti
kaikkiin dropdown-valikoihin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:52:54 +02:00
306dc6c5cc Automaattisäännöt: vastaanottaja-ehto, prioriteetti, abuse-tyyppi
- Lisää "Vastaanottaja sisältää" -ehto (to_contains) sääntöihin
- Lisää "Aseta prioriteetti" -toimenpide (set_priority)
- Lisää "Abuse" tikettityyppi
- Korjaa DB-schema: subject_contains, to_contains, enabled, set_priority, set_tags sarakkeet
- Parsii To-headerit sähköposteista säännön matchausta varten
- Mahdollistaa esim. abuse@-postien automaattisen tyypityksen ja prioriteetin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:27:29 +02:00
70bd095b24 NetAdmin: Gateway-kenttä, IPAM-integraatio VLAN/IP-tietoihin
- Lisää Gateway-sarake ja -valitsin NetAdmin-näkymään (devices-linkitys)
- VLAN ja IP näytetään IPAM:sta automaattisesti asiakkaan nimellä
- Muokkausmodaalissa asiakkaan IPAM VLANit/IP:t näkyvät ensimmäisinä
- DB: gateway_device_id LEFT JOIN devices, IPAM-enrichment API:ssa

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 11:09:52 +02:00
3fe45b217c Dokumenttikansiot asiakaskohtaisiksi
- Lisää customer_id sarake document_folders-tauluun (ALTER TABLE migraatio)
- dbLoadFolders() tukee nyt customer_id suodatusta
- dbSaveFolder() tallentaa customer_id:n kansioon
- API document_folders endpoint vastaanottaa customer_id parametrin
- JS: kansiot ladataan ja luodaan asiakaskohtaisesti (currentDocCustomerId)
- Jokaisen asiakkaan kansiorakenne on nyt itsenäinen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 10:04:03 +02:00
663c37c7a7 Korjaa laitetilan poisto: siivoa viittaukset ennen poistoa
- dbDeleteLaitetila nollaa devices.laitetila_id, devices.site_id
  ja ipam.site_id ennen laitetilan poistoa
- API: parempi virhekäsittely (\Throwable), logi, tyhjä ID tarkistus
- Tiedostojen poisto: @-suppression ja GLOB_BRACE hidden-tiedostoille

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 00:59:03 +02:00
8e9fa76f9c 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>
2026-03-12 00:54:03 +02:00
13feb31304 Laitetilat: laitelinkit kortteihin + Sijainnit pois yritysasetuksista
Laitetila-laite-yhteys:
- Lisätty laitetila_id -sarake devices-tauluun
- Laite-lomakkeeseen uusi "Laitetila" dropdown (Tekniikka → Laitteet)
- Laitetila-kortit näyttävät laitemäärän ja laitechipit (max 4 + "+N muuta")
- Laitetilan detailnäkymässä taulukko tilan laitteista (nimi, tyyppi, malli, IP, ping)
- dbLoadLaitetilat palauttaa device_count ja devices-listan per laitetila

Yritysasetukset:
- Poistettu Sijainnit-osio yrityksen tiedoista (hallitaan Tekniikka → Sijainnit)
- Sijainnit sub-tab Tekniikassa pysyy ennallaan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 00:39:45 +02:00
2cacea2a2c Liittymien VLAN/Laite/IP-kentät hakukentiksi + IP/verkko-tuki
- Korvattu select-dropdownit hakukentillä (searchable combobox)
  - Kirjoittamalla suodattaa tuloksia nimellä, IP:llä, sijainnilla jne.
  - Nuolinäppäimillä navigointi, Enter valitsee, Esc sulkee
  - Vapaan tekstin syöttö mahdollista jos IPAM:sta ei löydy
- IP-kenttä tukee nyt myös verkkoja (subnet/prefix) IP-osoitteiden lisäksi
  - Vapaat IP:t, varatut IP:t ja verkot ryhmitelty omiin osioihinsa
  - Badge-värit: vihreä (vapaa), punainen (varattu), sininen (subnet)
- Sama hakukenttä-komponentti sekä netadmin-modalissa että asiakasformissa
- API palauttaa nyt subnetit IP-listan mukana

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 00:29:05 +02:00