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>
This commit is contained in:
57
api.php
57
api.php
@@ -3256,7 +3256,7 @@ switch ($action) {
|
|||||||
$input = json_decode(file_get_contents('php://input'), true);
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
$id = $input['id'] ?? '';
|
$id = $input['id'] ?? '';
|
||||||
$type = $input['type'] ?? '';
|
$type = $input['type'] ?? '';
|
||||||
$validTypes = ['laskutus', 'tekniikka', 'vika', 'muu'];
|
$validTypes = array_map(fn($t) => $t['value'], dbLoadTicketTypes($companyId));
|
||||||
if (!in_array($type, $validTypes)) {
|
if (!in_array($type, $validTypes)) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
echo json_encode(['error' => 'Virheellinen tyyppi']);
|
echo json_encode(['error' => 'Virheellinen tyyppi']);
|
||||||
@@ -3511,6 +3511,61 @@ switch ($action) {
|
|||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'ticket_types':
|
||||||
|
requireAuth();
|
||||||
|
$companyId = requireCompany();
|
||||||
|
echo json_encode(dbLoadTicketTypes($companyId));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ticket_type_save':
|
||||||
|
requireAuth();
|
||||||
|
$companyId = requireCompany();
|
||||||
|
if ($method !== 'POST') break;
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$value = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($input['value'] ?? '')));
|
||||||
|
$label = trim($input['label'] ?? '');
|
||||||
|
if (!$value || !$label) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Tunnus ja nimi vaaditaan']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dbSaveTicketType($companyId, [
|
||||||
|
'id' => $input['id'] ?? null,
|
||||||
|
'value' => $value,
|
||||||
|
'label' => $label,
|
||||||
|
'color' => $input['color'] ?? '',
|
||||||
|
'sort_order' => intval($input['sort_order'] ?? 0),
|
||||||
|
]);
|
||||||
|
dbAddLog($companyId, currentUser(), 'config_update', '', '', 'Tikettityyppi: ' . $label);
|
||||||
|
echo json_encode(dbLoadTicketTypes($companyId));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ticket_type_delete':
|
||||||
|
requireAuth();
|
||||||
|
$companyId = requireCompany();
|
||||||
|
if ($method !== 'POST') break;
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$value = $input['value'] ?? '';
|
||||||
|
if (!$value) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Tyyppi puuttuu']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Tarkista onko käytössä
|
||||||
|
$tickets = dbLoadTickets($companyId);
|
||||||
|
$inUse = 0;
|
||||||
|
foreach ($tickets as $t) {
|
||||||
|
if (($t['type'] ?? '') === $value) $inUse++;
|
||||||
|
}
|
||||||
|
if ($inUse > 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => "Tyyppiä käytetään {$inUse} tiketissä, ei voi poistaa"]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dbDeleteTicketType($companyId, $value);
|
||||||
|
echo json_encode(dbLoadTicketTypes($companyId));
|
||||||
|
break;
|
||||||
|
|
||||||
case 'ticket_priority':
|
case 'ticket_priority':
|
||||||
requireAuth();
|
requireAuth();
|
||||||
$companyId = requireCompanyOrParam();
|
$companyId = requireCompanyOrParam();
|
||||||
|
|||||||
58
db.php
58
db.php
@@ -346,6 +346,18 @@ function initDatabase(): void {
|
|||||||
INDEX idx_company (company_id)
|
INDEX idx_company (company_id)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
|
||||||
|
|
||||||
|
"CREATE TABLE IF NOT EXISTS ticket_types (
|
||||||
|
id VARCHAR(20) PRIMARY KEY,
|
||||||
|
company_id VARCHAR(50) NOT NULL,
|
||||||
|
value VARCHAR(50) NOT NULL,
|
||||||
|
label VARCHAR(100) NOT NULL,
|
||||||
|
color VARCHAR(20) DEFAULT '',
|
||||||
|
sort_order INT DEFAULT 0,
|
||||||
|
UNIQUE KEY uk_company_value (company_id, value),
|
||||||
|
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
|
||||||
|
INDEX idx_company (company_id)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
|
||||||
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_priority_emails (
|
"CREATE TABLE IF NOT EXISTS customer_priority_emails (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
company_id VARCHAR(50) NOT NULL,
|
company_id VARCHAR(50) NOT NULL,
|
||||||
@@ -1582,6 +1594,52 @@ function dbDeleteTicketRule(string $ruleId): void {
|
|||||||
_dbExecute("DELETE FROM ticket_rules WHERE id = ?", [$ruleId]);
|
_dbExecute("DELETE FROM ticket_rules WHERE id = ?", [$ruleId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== TIKETTITYYPIT ====================
|
||||||
|
|
||||||
|
function dbLoadTicketTypes(string $companyId): array {
|
||||||
|
$types = _dbFetchAll("SELECT * FROM ticket_types WHERE company_id = ? ORDER BY sort_order, label", [$companyId]);
|
||||||
|
// Jos ei tyyppejä, luo oletukset
|
||||||
|
if (empty($types)) {
|
||||||
|
$defaults = [
|
||||||
|
['value' => 'laskutus', 'label' => 'Laskutus', 'sort_order' => 1],
|
||||||
|
['value' => 'tekniikka', 'label' => 'Tekniikka', 'sort_order' => 2],
|
||||||
|
['value' => 'vika', 'label' => 'Vika', 'sort_order' => 3],
|
||||||
|
['value' => 'abuse', 'label' => 'Abuse', 'sort_order' => 4],
|
||||||
|
['value' => 'muu', 'label' => 'Muu', 'sort_order' => 5],
|
||||||
|
];
|
||||||
|
foreach ($defaults as $d) {
|
||||||
|
dbSaveTicketType($companyId, $d);
|
||||||
|
}
|
||||||
|
$types = _dbFetchAll("SELECT * FROM ticket_types WHERE company_id = ? ORDER BY sort_order, label", [$companyId]);
|
||||||
|
}
|
||||||
|
foreach ($types as &$t) {
|
||||||
|
$t['sort_order'] = (int)($t['sort_order'] ?? 0);
|
||||||
|
unset($t['company_id']);
|
||||||
|
}
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbSaveTicketType(string $companyId, array $type): void {
|
||||||
|
$id = $type['id'] ?? generateId();
|
||||||
|
_dbExecute("
|
||||||
|
INSERT INTO ticket_types (id, company_id, value, label, color, sort_order)
|
||||||
|
VALUES (:id, :company_id, :value, :label, :color, :sort_order)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
label = VALUES(label), color = VALUES(color), sort_order = VALUES(sort_order)
|
||||||
|
", [
|
||||||
|
'id' => $id,
|
||||||
|
'company_id' => $companyId,
|
||||||
|
'value' => $type['value'] ?? '',
|
||||||
|
'label' => $type['label'] ?? '',
|
||||||
|
'color' => $type['color'] ?? '',
|
||||||
|
'sort_order' => $type['sort_order'] ?? 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbDeleteTicketType(string $companyId, string $value): void {
|
||||||
|
_dbExecute("DELETE FROM ticket_types WHERE company_id = ? AND value = ?", [$companyId, $value]);
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== YRITYKSEN API-ASETUKSET ====================
|
// ==================== YRITYKSEN API-ASETUKSET ====================
|
||||||
|
|
||||||
function dbGetCompanyConfig(string $companyId): array {
|
function dbGetCompanyConfig(string $companyId): array {
|
||||||
|
|||||||
297
index.html
297
index.html
@@ -1066,6 +1066,9 @@
|
|||||||
<div class="sub-tab-bar" id="support-sub-tab-bar">
|
<div class="sub-tab-bar" id="support-sub-tab-bar">
|
||||||
<button class="sub-tab active" data-support-subtab="support-tickets">Tiketit</button>
|
<button class="sub-tab active" data-support-subtab="support-tickets">Tiketit</button>
|
||||||
<button class="sub-tab" data-support-subtab="support-ohjeet">Ohjeet</button>
|
<button class="sub-tab" data-support-subtab="support-ohjeet">Ohjeet</button>
|
||||||
|
<button class="sub-tab" data-support-subtab="support-saannot">Säännöt</button>
|
||||||
|
<button class="sub-tab" data-support-subtab="support-vastauspohjat">Vastauspohjat</button>
|
||||||
|
<button class="sub-tab" data-support-subtab="support-asetukset">Asetukset</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="subtab-support-tickets" class="sub-tab-content active">
|
<div id="subtab-support-tickets" class="sub-tab-content active">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
@@ -1079,11 +1082,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<select id="ticket-type-filter" style="padding:9px 12px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.88rem;">
|
<select id="ticket-type-filter" style="padding:9px 12px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.88rem;">
|
||||||
<option value="">Kaikki tyypit</option>
|
<option value="">Kaikki tyypit</option>
|
||||||
<option value="laskutus">Laskutus</option>
|
|
||||||
<option value="tekniikka">Tekniikka</option>
|
|
||||||
<option value="vika">Vika</option>
|
|
||||||
<option value="abuse">Abuse</option>
|
|
||||||
<option value="muu">Muu</option>
|
|
||||||
</select>
|
</select>
|
||||||
<select id="ticket-status-filter" style="padding:9px 12px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.88rem;">
|
<select id="ticket-status-filter" style="padding:9px 12px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.88rem;">
|
||||||
<option value="">Kaikki avoimet</option>
|
<option value="">Kaikki avoimet</option>
|
||||||
@@ -1101,9 +1099,6 @@
|
|||||||
<label style="display:flex;align-items:center;gap:0.4rem;font-size:0.85rem;color:#777;cursor:pointer;white-space:nowrap;">
|
<label style="display:flex;align-items:center;gap:0.4rem;font-size:0.85rem;color:#777;cursor:pointer;white-space:nowrap;">
|
||||||
<input type="checkbox" id="ticket-show-closed"> Suljetut
|
<input type="checkbox" id="ticket-show-closed"> Suljetut
|
||||||
</label>
|
</label>
|
||||||
<button class="btn-secondary" id="btn-ticket-templates" style="padding:7px 14px;font-size:0.82rem;">📝 Vastauspohjat</button>
|
|
||||||
<button class="btn-secondary" id="btn-ticket-rules" style="padding:7px 14px;font-size:0.82rem;">⚙ Säännöt</button>
|
|
||||||
<button class="btn-secondary" id="btn-ticket-settings" style="padding:7px 14px;font-size:0.82rem;">⚙ Omat asetukset</button>
|
|
||||||
<label style="display:flex;align-items:center;gap:0.4rem;font-size:0.82rem;color:#777;cursor:pointer;white-space:nowrap;margin-left:auto;">
|
<label style="display:flex;align-items:center;gap:0.4rem;font-size:0.82rem;color:#777;cursor:pointer;white-space:nowrap;margin-left:auto;">
|
||||||
<input type="checkbox" id="ticket-auto-refresh" checked> Autopäivitys
|
<input type="checkbox" id="ticket-auto-refresh" checked> Autopäivitys
|
||||||
<select id="ticket-refresh-interval" style="padding:3px 6px;border:1px solid #ddd;border-radius:5px;font-size:0.8rem;">
|
<select id="ticket-refresh-interval" style="padding:3px 6px;border:1px solid #ddd;border-radius:5px;font-size:0.8rem;">
|
||||||
@@ -1193,150 +1188,154 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sääntönäkymä -->
|
|
||||||
<div id="ticket-rules-view" style="display:none;">
|
|
||||||
<button class="btn-secondary" id="btn-rules-back" style="color:#555;border-color:#ddd;margin-bottom:1rem;">← Takaisin tiketteihin</button>
|
|
||||||
<div class="table-card" style="padding:1.5rem;">
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
|
|
||||||
<h3 style="color:#0f3460;margin:0;">Automaattisäännöt</h3>
|
|
||||||
<button class="btn-primary" id="btn-add-rule" style="font-size:0.85rem;">+ Lisää sääntö</button>
|
|
||||||
</div>
|
|
||||||
<p style="color:#888;font-size:0.85rem;margin-bottom:1rem;">Säännöt soveltuvat automaattisesti uusiin tiketteihin haettaessa sähköposteja. Ensimmäinen täsmäävä sääntö voittaa.</p>
|
|
||||||
<div id="rules-list"></div>
|
|
||||||
</div>
|
|
||||||
<!-- Sääntölomake -->
|
|
||||||
<div id="rule-form-container" class="table-card" style="padding:1.5rem;margin-top:1rem;display:none;">
|
|
||||||
<h4 style="color:#0f3460;margin-bottom:1rem;" id="rule-form-title">Uusi sääntö</h4>
|
|
||||||
<input type="hidden" id="rule-form-id">
|
|
||||||
<div class="form-grid" style="max-width:600px;">
|
|
||||||
<div class="form-group full-width">
|
|
||||||
<label>Säännön nimi *</label>
|
|
||||||
<input type="text" id="rule-form-name" placeholder="esim. Sulje notifikaatiot">
|
|
||||||
</div>
|
|
||||||
<div class="form-group full-width" style="margin-top:0.5rem;">
|
|
||||||
<label style="font-weight:600;color:#0f3460;">Ehdot (kaikki täytetyt pitää täsmätä)</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Lähettäjä sisältää</label>
|
|
||||||
<input type="text" id="rule-form-from" placeholder="esim. noreply@">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Vastaanottaja sisältää</label>
|
|
||||||
<input type="text" id="rule-form-to" placeholder="esim. abuse@ tai laskutus@">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Otsikko sisältää</label>
|
|
||||||
<input type="text" id="rule-form-subject" placeholder="esim. saatavuuskysely">
|
|
||||||
</div>
|
|
||||||
<div class="form-group full-width" style="margin-top:0.5rem;">
|
|
||||||
<label style="font-weight:600;color:#0f3460;">Toimenpiteet</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Aseta tila</label>
|
|
||||||
<select id="rule-form-status">
|
|
||||||
<option value="">Ei muuteta</option>
|
|
||||||
<option value="suljettu">Suljettu</option>
|
|
||||||
<option value="kasittelyssa">Käsittelyssä</option>
|
|
||||||
<option value="ratkaistu">Ratkaistu</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Aseta tyyppi</label>
|
|
||||||
<select id="rule-form-type">
|
|
||||||
<option value="">Ei muuteta</option>
|
|
||||||
<option value="laskutus">Laskutus</option>
|
|
||||||
<option value="tekniikka">Tekniikka</option>
|
|
||||||
<option value="vika">Vika</option>
|
|
||||||
<option value="abuse">Abuse</option>
|
|
||||||
<option value="muu">Muu</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Aseta prioriteetti</label>
|
|
||||||
<select id="rule-form-priority">
|
|
||||||
<option value="">Ei muuteta</option>
|
|
||||||
<option value="normaali">Normaali</option>
|
|
||||||
<option value="tärkeä">Tärkeä</option>
|
|
||||||
<option value="urgent">Kiireellinen</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Aseta tagit (pilkulla eroteltuna)</label>
|
|
||||||
<input type="text" id="rule-form-tags" placeholder="esim. notification, automaatti">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Auto-close (päivää)</label>
|
|
||||||
<input type="number" id="rule-form-autoclose" min="0" max="365" placeholder="esim. 7" style="max-width:120px;">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
|
|
||||||
<button class="btn-primary" id="btn-save-rule">Tallenna</button>
|
|
||||||
<button class="btn-secondary" id="btn-cancel-rule">Peruuta</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Vastauspohjien hallinta -->
|
|
||||||
<div id="ticket-templates-view" style="display:none;">
|
|
||||||
<button class="btn-secondary" id="btn-templates-back" style="color:#555;border-color:#ddd;margin-bottom:1rem;">← Takaisin tiketteihin</button>
|
|
||||||
<div class="table-card" style="padding:1.5rem;">
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
|
|
||||||
<h3 style="color:#0f3460;margin:0;">Vastauspohjat</h3>
|
|
||||||
<button class="btn-primary" id="btn-add-tpl" style="font-size:0.85rem;">+ Lisää pohja</button>
|
|
||||||
</div>
|
|
||||||
<p style="color:#888;font-size:0.85rem;margin-bottom:1rem;">Yrityksen yhteiset vastauspohjat tiketteihin. Valittavissa vastauslomakkeen valikosta kaikille käyttäjille.</p>
|
|
||||||
<div id="tpl-list"></div>
|
|
||||||
</div>
|
|
||||||
<!-- Pohjanlomake -->
|
|
||||||
<div id="tpl-form-container" class="table-card" style="padding:1.5rem;margin-top:1rem;display:none;">
|
|
||||||
<h4 style="color:#0f3460;margin-bottom:1rem;" id="tpl-form-title">Uusi vastauspohja</h4>
|
|
||||||
<input type="hidden" id="tpl-form-id">
|
|
||||||
<div class="form-grid" style="max-width:600px;">
|
|
||||||
<div class="form-group full-width">
|
|
||||||
<label>Nimi *</label>
|
|
||||||
<input type="text" id="tpl-form-name" placeholder="esim. Kuittaus vastaanotettu">
|
|
||||||
</div>
|
|
||||||
<div class="form-group full-width">
|
|
||||||
<label>Sisältö *</label>
|
|
||||||
<textarea id="tpl-form-body" rows="6" placeholder="Kiitos viestistäsi! Olemme vastaanottaneet asiasi ja palaamme siihen mahdollisimman pian."></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
|
|
||||||
<button class="btn-primary" id="btn-save-tpl">Tallenna</button>
|
|
||||||
<button class="btn-secondary" id="btn-cancel-tpl">Peruuta</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Omat asetukset -näkymä -->
|
|
||||||
<div id="ticket-settings-view" style="display:none;">
|
|
||||||
<div style="display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem;">
|
|
||||||
<button class="btn-secondary" onclick="closeTicketSettings()">← Takaisin</button>
|
|
||||||
<h2 style="color:#0f3460;font-size:1.15rem;margin:0;">Omat asetukset</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-card" style="padding:1.5rem;margin-bottom:1.5rem;">
|
|
||||||
<h3 style="color:#0f3460;font-size:1rem;margin-bottom:0.75rem;">Sähköpostiallekirjoitukset</h3>
|
|
||||||
<p style="color:#888;font-size:0.82rem;margin-bottom:1rem;">Allekirjoitus liitetään automaattisesti sähköpostivastausten loppuun.</p>
|
|
||||||
<div id="ticket-settings-signatures"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-card" style="padding:1.5rem;margin-bottom:1.5rem;">
|
|
||||||
<h3 style="color:#0f3460;font-size:1rem;margin-bottom:0.75rem;">Postilaatikoiden näkyvyys</h3>
|
|
||||||
<p style="color:#888;font-size:0.82rem;margin-bottom:1rem;">Poista rasti postilaatikoista joiden tikettejä et halua nähdä.</p>
|
|
||||||
<div id="ticket-settings-mailbox-visibility"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display:flex;gap:0.75rem;">
|
|
||||||
<button class="btn-primary" id="btn-save-ticket-settings">Tallenna asetukset</button>
|
|
||||||
<button class="btn-secondary" onclick="closeTicketSettings()">Peruuta</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /subtab-support-tickets -->
|
</div><!-- /subtab-support-tickets -->
|
||||||
|
|
||||||
|
<!-- Sub-tab: Säännöt -->
|
||||||
|
<div id="subtab-support-saannot" class="sub-tab-content">
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="table-card" style="padding:1.5rem;">
|
||||||
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
|
||||||
|
<h3 style="color:#0f3460;margin:0;">Automaattisäännöt</h3>
|
||||||
|
<button class="btn-primary" id="btn-add-rule" style="font-size:0.85rem;">+ Lisää sääntö</button>
|
||||||
|
</div>
|
||||||
|
<p style="color:#888;font-size:0.85rem;margin-bottom:1rem;">Säännöt soveltuvat automaattisesti uusiin tiketteihin haettaessa sähköposteja. Ensimmäinen täsmäävä sääntö voittaa.</p>
|
||||||
|
<div id="rules-list"></div>
|
||||||
|
</div>
|
||||||
|
<!-- Sääntölomake -->
|
||||||
|
<div id="rule-form-container" class="table-card" style="padding:1.5rem;margin-top:1rem;display:none;">
|
||||||
|
<h4 style="color:#0f3460;margin-bottom:1rem;" id="rule-form-title">Uusi sääntö</h4>
|
||||||
|
<input type="hidden" id="rule-form-id">
|
||||||
|
<div class="form-grid" style="max-width:600px;">
|
||||||
|
<div class="form-group full-width">
|
||||||
|
<label>Säännön nimi *</label>
|
||||||
|
<input type="text" id="rule-form-name" placeholder="esim. Sulje notifikaatiot">
|
||||||
|
</div>
|
||||||
|
<div class="form-group full-width" style="margin-top:0.5rem;">
|
||||||
|
<label style="font-weight:600;color:#0f3460;">Ehdot (kaikki täytetyt pitää täsmätä)</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Lähettäjä sisältää</label>
|
||||||
|
<input type="text" id="rule-form-from" placeholder="esim. noreply@">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Vastaanottaja sisältää</label>
|
||||||
|
<input type="text" id="rule-form-to" placeholder="esim. abuse@ tai laskutus@">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Otsikko sisältää</label>
|
||||||
|
<input type="text" id="rule-form-subject" placeholder="esim. saatavuuskysely">
|
||||||
|
</div>
|
||||||
|
<div class="form-group full-width" style="margin-top:0.5rem;">
|
||||||
|
<label style="font-weight:600;color:#0f3460;">Toimenpiteet</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Aseta tila</label>
|
||||||
|
<select id="rule-form-status">
|
||||||
|
<option value="">Ei muuteta</option>
|
||||||
|
<option value="suljettu">Suljettu</option>
|
||||||
|
<option value="kasittelyssa">Käsittelyssä</option>
|
||||||
|
<option value="ratkaistu">Ratkaistu</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Aseta tyyppi</label>
|
||||||
|
<select id="rule-form-type">
|
||||||
|
<option value="">Ei muuteta</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Aseta prioriteetti</label>
|
||||||
|
<select id="rule-form-priority">
|
||||||
|
<option value="">Ei muuteta</option>
|
||||||
|
<option value="normaali">Normaali</option>
|
||||||
|
<option value="tärkeä">Tärkeä</option>
|
||||||
|
<option value="urgent">Kiireellinen</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Aseta tagit (pilkulla eroteltuna)</label>
|
||||||
|
<input type="text" id="rule-form-tags" placeholder="esim. notification, automaatti">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Auto-close (päivää)</label>
|
||||||
|
<input type="number" id="rule-form-autoclose" min="0" max="365" placeholder="esim. 7" style="max-width:120px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
|
||||||
|
<button class="btn-primary" id="btn-save-rule">Tallenna</button>
|
||||||
|
<button class="btn-secondary" id="btn-cancel-rule">Peruuta</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tikettityyppien hallinta -->
|
||||||
|
<div class="table-card" style="padding:1.5rem;margin-top:1rem;">
|
||||||
|
<h4 style="color:#0f3460;margin-bottom:0.5rem;">Tikettityypit</h4>
|
||||||
|
<p style="color:#888;font-size:0.85rem;margin-bottom:1rem;">Hallitse yrityksen tikettityyppejä. Käytössä olevia tyyppejä ei voi poistaa.</p>
|
||||||
|
<div id="ticket-types-list"></div>
|
||||||
|
<div style="display:flex;gap:0.5rem;margin-top:0.75rem;align-items:center;">
|
||||||
|
<input type="text" id="new-ticket-type-value" placeholder="tunnus (esim. myynti)" style="max-width:160px;">
|
||||||
|
<input type="text" id="new-ticket-type-label" placeholder="Nimi (esim. Myynti)" style="max-width:160px;">
|
||||||
|
<button class="btn-primary" id="btn-add-ticket-type" style="font-size:0.85rem;">+ Lisää</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /subtab-support-saannot -->
|
||||||
|
|
||||||
|
<!-- Sub-tab: Vastauspohjat -->
|
||||||
|
<div id="subtab-support-vastauspohjat" class="sub-tab-content">
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="table-card" style="padding:1.5rem;">
|
||||||
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
|
||||||
|
<h3 style="color:#0f3460;margin:0;">Vastauspohjat</h3>
|
||||||
|
<button class="btn-primary" id="btn-add-tpl" style="font-size:0.85rem;">+ Lisää pohja</button>
|
||||||
|
</div>
|
||||||
|
<p style="color:#888;font-size:0.85rem;margin-bottom:1rem;">Yrityksen yhteiset vastauspohjat tiketteihin. Valittavissa vastauslomakkeen valikosta kaikille käyttäjille.</p>
|
||||||
|
<div id="tpl-list"></div>
|
||||||
|
</div>
|
||||||
|
<!-- Pohjanlomake -->
|
||||||
|
<div id="tpl-form-container" class="table-card" style="padding:1.5rem;margin-top:1rem;display:none;">
|
||||||
|
<h4 style="color:#0f3460;margin-bottom:1rem;" id="tpl-form-title">Uusi vastauspohja</h4>
|
||||||
|
<input type="hidden" id="tpl-form-id">
|
||||||
|
<div class="form-grid" style="max-width:600px;">
|
||||||
|
<div class="form-group full-width">
|
||||||
|
<label>Nimi *</label>
|
||||||
|
<input type="text" id="tpl-form-name" placeholder="esim. Kuittaus vastaanotettu">
|
||||||
|
</div>
|
||||||
|
<div class="form-group full-width">
|
||||||
|
<label>Sisältö *</label>
|
||||||
|
<textarea id="tpl-form-body" rows="6" placeholder="Kiitos viestistäsi! Olemme vastaanottaneet asiasi ja palaamme siihen mahdollisimman pian."></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
|
||||||
|
<button class="btn-primary" id="btn-save-tpl">Tallenna</button>
|
||||||
|
<button class="btn-secondary" id="btn-cancel-tpl">Peruuta</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /subtab-support-vastauspohjat -->
|
||||||
|
|
||||||
|
<!-- Sub-tab: Asetukset -->
|
||||||
|
<div id="subtab-support-asetukset" class="sub-tab-content">
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="table-card" style="padding:1.5rem;margin-bottom:1.5rem;">
|
||||||
|
<h3 style="color:#0f3460;font-size:1rem;margin-bottom:0.75rem;">Sähköpostiallekirjoitukset</h3>
|
||||||
|
<p style="color:#888;font-size:0.82rem;margin-bottom:1rem;">Allekirjoitus liitetään automaattisesti sähköpostivastausten loppuun.</p>
|
||||||
|
<div id="ticket-settings-signatures"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-card" style="padding:1.5rem;margin-bottom:1.5rem;">
|
||||||
|
<h3 style="color:#0f3460;font-size:1rem;margin-bottom:0.75rem;">Postilaatikoiden näkyvyys</h3>
|
||||||
|
<p style="color:#888;font-size:0.82rem;margin-bottom:1rem;">Poista rasti postilaatikoista joiden tikettejä et halua nähdä.</p>
|
||||||
|
<div id="ticket-settings-mailbox-visibility"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:flex;gap:0.75rem;">
|
||||||
|
<button class="btn-primary" id="btn-save-ticket-settings">Tallenna asetukset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /subtab-support-asetukset -->
|
||||||
|
|
||||||
<!-- Sub-tab: Ohjeet -->
|
<!-- Sub-tab: Ohjeet -->
|
||||||
<div id="subtab-support-ohjeet" class="sub-tab-content">
|
<div id="subtab-support-ohjeet" class="sub-tab-content">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
|
|||||||
162
script.js
162
script.js
@@ -301,11 +301,8 @@ function switchToTab(target, subTab) {
|
|||||||
if (target === 'support') {
|
if (target === 'support') {
|
||||||
loadTickets(); showTicketListView();
|
loadTickets(); showTicketListView();
|
||||||
if (document.getElementById('ticket-auto-refresh').checked) startTicketAutoRefresh();
|
if (document.getElementById('ticket-auto-refresh').checked) startTicketAutoRefresh();
|
||||||
if (subTab === 'ohjeet') {
|
const supportSubMap = { ohjeet: 'support-ohjeet', saannot: 'support-saannot', vastauspohjat: 'support-vastauspohjat', asetukset: 'support-asetukset' };
|
||||||
switchSupportSubTab('support-ohjeet');
|
switchSupportSubTab(supportSubMap[subTab] || 'support-tickets');
|
||||||
} else {
|
|
||||||
switchSupportSubTab('support-tickets');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (target === 'documents') {
|
if (target === 'documents') {
|
||||||
if (subTab && subTab !== 'kokoukset') {
|
if (subTab && subTab !== 'kokoukset') {
|
||||||
@@ -1344,7 +1341,7 @@ const ticketStatusLabels = {
|
|||||||
suljettu: 'Suljettu',
|
suljettu: 'Suljettu',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ticketTypeLabels = {
|
let ticketTypeLabels = {
|
||||||
laskutus: 'Laskutus',
|
laskutus: 'Laskutus',
|
||||||
tekniikka: 'Tekniikka',
|
tekniikka: 'Tekniikka',
|
||||||
vika: 'Vika',
|
vika: 'Vika',
|
||||||
@@ -1520,10 +1517,9 @@ async function showTicketDetail(id, companyId = '') {
|
|||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
|
<div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
|
||||||
<select id="ticket-type-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
<select id="ticket-type-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
||||||
<option value="muu" ${(ticket.type || 'muu') === 'muu' ? 'selected' : ''}>Muu</option>
|
${Object.entries(ticketTypeLabels).map(([val, label]) =>
|
||||||
<option value="laskutus" ${ticket.type === 'laskutus' ? 'selected' : ''}>Laskutus</option>
|
`<option value="${val}" ${(ticket.type || 'muu') === val ? 'selected' : ''}>${esc(label)}</option>`
|
||||||
<option value="tekniikka" ${ticket.type === 'tekniikka' ? 'selected' : ''}>Tekniikka</option>
|
).join('')}
|
||||||
<option value="vika" ${ticket.type === 'vika' ? 'selected' : ''}>Vika</option>
|
|
||||||
</select>
|
</select>
|
||||||
<select id="ticket-status-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
<select id="ticket-status-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
||||||
<option value="uusi" ${ticket.status === 'uusi' ? 'selected' : ''}>Uusi</option>
|
<option value="uusi" ${ticket.status === 'uusi' ? 'selected' : ''}>Uusi</option>
|
||||||
@@ -2022,20 +2018,6 @@ function renderRules() {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRulesView() {
|
|
||||||
document.getElementById('ticket-list-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-detail-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-templates-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-settings-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-rules-view').style.display = 'block';
|
|
||||||
loadRules();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideRulesView() {
|
|
||||||
document.getElementById('ticket-rules-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-list-view').style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function showRuleForm(rule) {
|
function showRuleForm(rule) {
|
||||||
document.getElementById('rule-form-container').style.display = '';
|
document.getElementById('rule-form-container').style.display = '';
|
||||||
document.getElementById('rule-form-title').textContent = rule ? 'Muokkaa sääntöä' : 'Uusi sääntö';
|
document.getElementById('rule-form-title').textContent = rule ? 'Muokkaa sääntöä' : 'Uusi sääntö';
|
||||||
@@ -2057,8 +2039,6 @@ function hideRuleForm() {
|
|||||||
editingRuleId = null;
|
editingRuleId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('btn-ticket-rules').addEventListener('click', () => showRulesView());
|
|
||||||
document.getElementById('btn-rules-back').addEventListener('click', () => hideRulesView());
|
|
||||||
document.getElementById('btn-add-rule').addEventListener('click', () => showRuleForm(null));
|
document.getElementById('btn-add-rule').addEventListener('click', () => showRuleForm(null));
|
||||||
document.getElementById('btn-cancel-rule').addEventListener('click', () => hideRuleForm());
|
document.getElementById('btn-cancel-rule').addEventListener('click', () => hideRuleForm());
|
||||||
|
|
||||||
@@ -2108,23 +2088,86 @@ async function toggleRule(id, enabled) {
|
|||||||
} catch (e) { alert(e.message); }
|
} catch (e) { alert(e.message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== TIKETTITYYPIT ====================
|
||||||
|
|
||||||
|
async function loadTicketTypes() {
|
||||||
|
try {
|
||||||
|
const types = await apiCall('ticket_types');
|
||||||
|
ticketTypeLabels = {};
|
||||||
|
types.forEach(t => { ticketTypeLabels[t.value] = t.label; });
|
||||||
|
renderTicketTypes(types);
|
||||||
|
populateTypeDropdowns();
|
||||||
|
} catch (e) { console.error('loadTicketTypes:', e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTicketTypes(types) {
|
||||||
|
const container = document.getElementById('ticket-types-list');
|
||||||
|
if (!container) return;
|
||||||
|
if (!types || types.length === 0) {
|
||||||
|
container.innerHTML = '<p style="color:#888;font-size:0.85rem;">Ei tikettityyppejä.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.innerHTML = types.map(t =>
|
||||||
|
`<div class="ticket-type-item">
|
||||||
|
<span class="ticket-type-badge ticket-type-${t.value}">${esc(t.label)}</span>
|
||||||
|
<span style="color:#888;font-size:0.8rem;">(${esc(t.value)})</span>
|
||||||
|
<button class="btn-danger" onclick="deleteTicketType('${esc(t.value)}')" style="padding:2px 8px;font-size:0.75rem;margin-left:auto;">Poista</button>
|
||||||
|
</div>`
|
||||||
|
).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateTypeDropdowns() {
|
||||||
|
const options = Object.entries(ticketTypeLabels).map(
|
||||||
|
([val, label]) => `<option value="${val}">${esc(label)}</option>`
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
// Tikettilistan suodatin
|
||||||
|
const filter = document.getElementById('ticket-type-filter');
|
||||||
|
if (filter) {
|
||||||
|
const current = filter.value;
|
||||||
|
filter.innerHTML = '<option value="">Kaikki tyypit</option>' + options;
|
||||||
|
filter.value = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sääntölomakkeen tyyppi
|
||||||
|
const ruleType = document.getElementById('rule-form-type');
|
||||||
|
if (ruleType) {
|
||||||
|
const current = ruleType.value;
|
||||||
|
ruleType.innerHTML = '<option value="">— Ei muuteta —</option>' + options;
|
||||||
|
ruleType.value = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tiketin detail-näkymän tyyppi-select
|
||||||
|
const detailType = document.getElementById('ticket-detail-type');
|
||||||
|
if (detailType) {
|
||||||
|
const current = detailType.value;
|
||||||
|
detailType.innerHTML = options;
|
||||||
|
detailType.value = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('btn-add-ticket-type')?.addEventListener('click', async () => {
|
||||||
|
const value = document.getElementById('new-ticket-type-value').value.trim().toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
||||||
|
const label = document.getElementById('new-ticket-type-label').value.trim();
|
||||||
|
if (!value || !label) { alert('Täytä tunnus ja nimi'); return; }
|
||||||
|
try {
|
||||||
|
await apiCall('ticket_type_save', 'POST', { value, label });
|
||||||
|
document.getElementById('new-ticket-type-value').value = '';
|
||||||
|
document.getElementById('new-ticket-type-label').value = '';
|
||||||
|
await loadTicketTypes();
|
||||||
|
} catch (e) { alert(e.message); }
|
||||||
|
});
|
||||||
|
|
||||||
|
window.deleteTicketType = async function(value) {
|
||||||
|
if (!confirm(`Poistetaanko tikettityyppi "${value}"?`)) return;
|
||||||
|
try {
|
||||||
|
await apiCall('ticket_type_delete', 'POST', { value });
|
||||||
|
await loadTicketTypes();
|
||||||
|
} catch (e) { alert(e.message); }
|
||||||
|
};
|
||||||
|
|
||||||
// ==================== VASTAUSPOHJAT (TUKITABISSA) ====================
|
// ==================== VASTAUSPOHJAT (TUKITABISSA) ====================
|
||||||
|
|
||||||
function showTemplatesView() {
|
|
||||||
document.getElementById('ticket-list-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-detail-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-rules-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-settings-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-templates-view').style.display = 'block';
|
|
||||||
hideTplForm();
|
|
||||||
renderTplList();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideTemplatesView() {
|
|
||||||
document.getElementById('ticket-templates-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-list-view').style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderTplList() {
|
function renderTplList() {
|
||||||
const list = document.getElementById('tpl-list');
|
const list = document.getElementById('tpl-list');
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
@@ -2158,11 +2201,6 @@ function hideTplForm() {
|
|||||||
document.getElementById('tpl-form-container').style.display = 'none';
|
document.getElementById('tpl-form-container').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('btn-ticket-templates').addEventListener('click', async () => {
|
|
||||||
await loadTemplates();
|
|
||||||
showTemplatesView();
|
|
||||||
});
|
|
||||||
document.getElementById('btn-templates-back').addEventListener('click', () => hideTemplatesView());
|
|
||||||
document.getElementById('btn-add-tpl').addEventListener('click', () => showTplForm(null));
|
document.getElementById('btn-add-tpl').addEventListener('click', () => showTplForm(null));
|
||||||
document.getElementById('btn-cancel-tpl').addEventListener('click', () => hideTplForm());
|
document.getElementById('btn-cancel-tpl').addEventListener('click', () => hideTplForm());
|
||||||
|
|
||||||
@@ -2195,14 +2233,7 @@ window.deleteTpl = async function(id) {
|
|||||||
|
|
||||||
// ==================== OMAT ASETUKSET (TIKETTIEN ASETUKSET) ====================
|
// ==================== OMAT ASETUKSET (TIKETTIEN ASETUKSET) ====================
|
||||||
|
|
||||||
async function openTicketSettings() {
|
async function initTicketSettings() {
|
||||||
// Piilota muut näkymät, näytä asetukset
|
|
||||||
document.getElementById('ticket-list-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-detail-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-rules-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-templates-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-settings-view').style.display = 'block';
|
|
||||||
|
|
||||||
const sigContainer = document.getElementById('ticket-settings-signatures');
|
const sigContainer = document.getElementById('ticket-settings-signatures');
|
||||||
const visContainer = document.getElementById('ticket-settings-mailbox-visibility');
|
const visContainer = document.getElementById('ticket-settings-mailbox-visibility');
|
||||||
sigContainer.innerHTML = '<p style="color:#888;font-size:0.85rem;">Ladataan...</p>';
|
sigContainer.innerHTML = '<p style="color:#888;font-size:0.85rem;">Ladataan...</p>';
|
||||||
@@ -2239,13 +2270,6 @@ async function openTicketSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeTicketSettings() {
|
|
||||||
document.getElementById('ticket-settings-view').style.display = 'none';
|
|
||||||
document.getElementById('ticket-list-view').style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('btn-ticket-settings').addEventListener('click', () => openTicketSettings());
|
|
||||||
|
|
||||||
document.getElementById('btn-save-ticket-settings').addEventListener('click', async () => {
|
document.getElementById('btn-save-ticket-settings').addEventListener('click', async () => {
|
||||||
// Kerää allekirjoitukset
|
// Kerää allekirjoitukset
|
||||||
const signatures = {};
|
const signatures = {};
|
||||||
@@ -2267,7 +2291,6 @@ document.getElementById('btn-save-ticket-settings').addEventListener('click', as
|
|||||||
// Päivitä lokaalit muuttujat
|
// Päivitä lokaalit muuttujat
|
||||||
currentUserSignatures = signatures;
|
currentUserSignatures = signatures;
|
||||||
currentHiddenMailboxes = hiddenMailboxes;
|
currentHiddenMailboxes = hiddenMailboxes;
|
||||||
closeTicketSettings();
|
|
||||||
// Lataa tiketit uudelleen suodatuksen päivittämiseksi
|
// Lataa tiketit uudelleen suodatuksen päivittämiseksi
|
||||||
loadTickets();
|
loadTickets();
|
||||||
alert('Asetukset tallennettu!');
|
alert('Asetukset tallennettu!');
|
||||||
@@ -3052,8 +3075,19 @@ function switchSupportSubTab(target) {
|
|||||||
if (btn) btn.classList.add('active');
|
if (btn) btn.classList.add('active');
|
||||||
const content = document.getElementById('subtab-' + target);
|
const content = document.getElementById('subtab-' + target);
|
||||||
if (content) content.classList.add('active');
|
if (content) content.classList.add('active');
|
||||||
if (target === 'support-ohjeet') { loadGuides(); window.location.hash = 'support/ohjeet'; }
|
// Lataa data tarvittaessa
|
||||||
else { window.location.hash = 'support'; }
|
const hashMap = {
|
||||||
|
'support-tickets': 'support',
|
||||||
|
'support-ohjeet': 'support/ohjeet',
|
||||||
|
'support-saannot': 'support/saannot',
|
||||||
|
'support-vastauspohjat': 'support/vastauspohjat',
|
||||||
|
'support-asetukset': 'support/asetukset',
|
||||||
|
};
|
||||||
|
if (target === 'support-ohjeet') loadGuides();
|
||||||
|
if (target === 'support-saannot') { loadRules(); loadTicketTypes(); }
|
||||||
|
if (target === 'support-vastauspohjat') loadTemplates();
|
||||||
|
if (target === 'support-asetukset') initTicketSettings();
|
||||||
|
window.location.hash = hashMap[target] || 'support';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelectorAll('#support-sub-tab-bar .sub-tab').forEach(btn => {
|
document.querySelectorAll('#support-sub-tab-bar .sub-tab').forEach(btn => {
|
||||||
|
|||||||
17
style.css
17
style.css
@@ -1355,6 +1355,23 @@ span.empty {
|
|||||||
color: #757575;
|
color: #757575;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket-type-abuse {
|
||||||
|
background: #fff3e0;
|
||||||
|
color: #e65100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-type-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-type-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Active (käsittelyssä) ticket row — green tint */
|
/* Active (käsittelyssä) ticket row — green tint */
|
||||||
.ticket-row-active {
|
.ticket-row-active {
|
||||||
background: #e8f8e8 !important;
|
background: #e8f8e8 !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user