Commit Graph

274 Commits

Author SHA1 Message Date
6fdfc9ec97 Fix Zammad sync overwriting local ticket status
Local statuses (odottaa vastausta, kasittelyssa, odottaa) set in the
intra UI were being overwritten by Zammad's state on every sync.
Now these statuses are preserved unless Zammad explicitly closes the ticket.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:27:35 +02:00
f24123be81 Convert iRedMail from REST API to direct MySQL (vmail DB) + per-company integration
- Replace IRedMailClient REST API class with direct MySQL/PDO connection to vmail database
- Move iRedMail config from global config table to per-company integrations (like Zammad)
- Add iRedMail integration card to API settings with DB host/name/user/password/port fields
- Add iRedMail checkbox to integrations section in company settings
- Change Hallinta tab visibility: show for admins (not just superadmins) when module enabled
- API endpoints now use requireCompany() + requireSuperAdmin() and get config from integrations
- Password hashing uses SSHA512 (iRedMail default)
- Mask db_password in API responses (like token masking for Zammad)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:31:23 +02:00
50f34ac37b Add module visibility fix and configurable postal codes for saatavuus API
- Add 'hallinta' to ALL_MODULES so it appears in company settings
- Change fallback from ALL_MODULES to DEFAULT_MODULES (new modules not enabled by default)
- Hallinta tab requires both module enabled + superadmin role
- Add per-company configurable postal codes for "todennäköinen" availability
- Saatavuus API returns true/todennäköinen/false based on customer data + postal codes
- Show "Todennäköinen" badge in availability queries list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:10:21 +02:00
6ea62b075f Add Hallinta module with iRedMail email management
New "Hallinta" main tab (superadmin only) with "Sähköposti" sub-tab for
managing email via iRedAdmin-Pro REST API. Features:
- IRedMailClient PHP class with cookie-based session auth + auto-retry
- Domain CRUD (list, create, delete)
- Mailbox CRUD (list, create, delete, password change)
- Alias CRUD (list, create, delete)
- Configuration modal (API URL, admin credentials, connection test)
- Search/filter for mailboxes
- 13 new API endpoints, all requireSuperAdmin()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:58:10 +02:00
b3d8b7e067 Simplify timer: checkbox + delay days, execute at 10:00
Timer is now a simple checkbox + days field. When enabled, rule actions
are scheduled for X days after ticket creation at 10:00 AM instead of
applying immediately. Removed no_activity condition in favor of simple
time-based scheduling stored on the ticket (delay_rule_id + delay_execute_at).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:35:45 +02:00
95434a42fe Add timer/scheduler to ticket automation rules
Adds optional delay_days + delay_condition (no_activity) to ticket rules.
When a ticket has no activity for X days, timed rules automatically apply
actions (e.g., escalate priority to urgent). Checked on each ticket list load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:24:57 +02:00
1a97e07768 Fix undefined function dbGetCompany → use dbLoadCompanies
sendTelegramAlert and telegram_test used non-existent dbGetCompany().
Replaced with dbLoadCompanies() + loop to find company name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:11:48 +02:00
acd3591544 Make bot token also per-company with global fallback
Both bot_token and chat_id are now per-company. If a company
doesn't set its own bot_token, falls back to the global one.
Removed parenthetical hints from labels, all admins can now
configure their own Telegram bot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:08:58 +02:00
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
77dc790b0f Add mobile responsiveness + PWA support
Mobile (768px):
- Tab bar: horizontal scroll with momentum, hidden scrollbar
- Header: compact layout, smaller fonts
- Tables: horizontal scroll, hide-mobile class for less important cols
- Modals: bottom-sheet style, full width
- Touch targets: min-height 36px for buttons/selects
- Toolbar: stacks vertically
- Stat cards: compact

Small phone (480px):
- Even more compact header and tabs
- Smaller table cells
- iOS zoom prevention (16px font on inputs)

PWA:
- manifest.json for Add to Home Screen
- apple-mobile-web-app-capable meta tags
- theme-color for status bar
- overscroll-behavior: none (prevent pull-to-refresh)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:33:48 +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
e2f08304b3 Add deep linking to tickets for easy sharing
Tickets now get a unique URL hash (#support/ticket/ID) when opened.
Copy link button in ticket header copies the full URL to clipboard.
Opening a ticket link navigates directly to the ticket detail view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:20:12 +02:00
f8073a2350 Poista ticket-threadin max-height scroll — koko sivu scrollaa
Viestiketju oli rajoitettu 60vh laatikkoon overflow-y:auto,
joka pakotti scrollaamaan iframemaisesti. Nyt viestit ovat
osa normaalia sivun virtausta.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:29:03 +02:00
3c0cf94f6e Näytä Kehitysehdotukset-alinavi Tehtävät-välilehdellä
Sub-tab-bar oli piilotettu display:none — nyt näkyvillä
samalla tyylillä kuin muissa näkymissä.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:27:22 +02:00
4c7e57da2a Sulje-nappi harmaaksi samalla värillä kuin suljettu-badge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:25:47 +02:00
8f5c269c66 Vaihda Tehtävä-napin teksti -> Lisää tehtäväksi
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:23:46 +02:00
6c7fd3289a Sulje-nappi harmaaksi ja X-ikoni pois
Tikettinäkymän Sulje-nappi neutraaliksi btn-secondary-tyylillä
ilman punaista väritystä ja ✕-merkkiä.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:22:58 +02:00
9683e0b552 Vaihda tiketin Poista-nappi Sulje-napiksi
Tiketti merkataan suljetuksi poistamisen sijaan. Ei enää confirm-dialogia.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:13:01 +02:00
79ec93b293 Lisää saatavuus-tikettityyppi + fallback-väri käsin lisätyille tyypeille
- Saatavuus-tyyppi oletuksiin (teal-väri)
- Fallback CSS: tuntemattomat tyypit saavat violetin taustan
- TypeLabel näyttää tyypin nimen isolla alkukirjaimella jos ei löydy labelista

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:24:52 +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
0c9a60a90b Siistimpi Hae postit -UX: ei välivaihe-tekstiä, vain vihreä tuloslaatikko
- Poistettu "Haetaan sähköpostit ja synkataan Zammad..." -teksti
- Status-laatikko näytetään vasta kun tulokset on valmiit
- scrollIntoView scrollaa tuloksen näkyviin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:03:44 +02:00
682de29e5a Piilota sidebar-tilastot + laskutus kun hinnat piilossa, oletusnäkymä support
- prices-hidden blurraa nyt myös sidebar-stats (liittymät, laskutus, keskihinta, nopeudet)
- Summary-barin laskutus blurrautuu samalla
- Oletusnäkymä kirjautumisen jälkeen vaihdettu customers → support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:59:39 +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
1b5b870ea9 Korjaa tikettiviestiluokittelu: outgoing-tyyppi tunnistetaan nyt lähteviksi
Zammad-synkasta tulevat viestit käyttävät tyyppiä 'outgoing' (ei 'reply_out'),
joten ne näkyivät virheellisesti saapuvina (sininen). Nyt molemmat tyypit
tunnistetaan lähteviksi ja näytetään vihreällä taustalla.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:44:10 +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
c4e7f45bca Korjaa allekirjoituksen valinta vastattaessa tiketteihin
Poistettu virheellinen fallback joka valitsi ensimmäisen satunnaisen
allekirjoituksen (esim. serverihuone.com) väärään tikettiin. Nyt
generoidaan oikea oletusallekirjoitus tiketin kontekstin perusteella
(käyttäjänimi + yritys + vastaanottava sähköposti + puhelin).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:47:58 +02:00
627bec1f47 Siirrä Lisää asiakas -nappi Asiakkaat-moduulin toolbariin
Nappi siirretty oikeasta yläkulmasta (header) asiakaslistauksen
hakupalkin viereen, missä se on loogisemmassa paikassa.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:35:33 +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
a105ec7cd9 Oletusallekirjoitus kaikille postilaatikoille ja Zammad-sähköposteille
Jos käyttäjä ei ole vielä tallentanut allekirjoitusta, näytetään
oletuksena: Käyttäjänimi / Yrityksen nimi / sähköpostiosoite.
Toimii sekä IMAP/SMTP-postilaatikoille että Zammad-sähköposteille.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:18:58 +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
d3ab0d3e76 Korjaa HTML-renderöinti viesteissä + mailbox-valinta Zammad-tiketeille
1. Sähköpostiviestien HTML renderöidään oikein (br, p, div, a, b, i jne.)
   sen sijaan että tagit näkyisivät raakatekstinä. Sanitoi vaarallisen
   sisällön (script, iframe, on*-attribuutit) pois turvallisesti.
2. Zammad-tiketeillä ei ole mailbox_id:tä, joten aiemmin valittiin aina
   listan ensimmäinen (tuki@serverihuone.com). Nyt näytetään
   "Valitse lähettäjä" placeholder kunnes käyttäjä valitsee oikean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:00:19 +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
c002072367 Lisää cache-busting versio script.js ja style.css latauksiin
Selain cachetti vanhan script.js:n aiheuttaen showClosed-virheen
vaikka korjaus oli jo palvelimella. Versioparametri pakottaa uuden
latauksen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:42:36 +02:00