diff --git a/api.php b/api.php
index cf98656..8b424a2 100644
--- a/api.php
+++ b/api.php
@@ -3256,7 +3256,7 @@ switch ($action) {
$input = json_decode(file_get_contents('php://input'), true);
$id = $input['id'] ?? '';
$type = $input['type'] ?? '';
- $validTypes = ['laskutus', 'tekniikka', 'vika', 'muu'];
+ $validTypes = array_map(fn($t) => $t['value'], dbLoadTicketTypes($companyId));
if (!in_array($type, $validTypes)) {
http_response_code(400);
echo json_encode(['error' => 'Virheellinen tyyppi']);
@@ -3511,6 +3511,61 @@ switch ($action) {
echo json_encode(['success' => true]);
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':
requireAuth();
$companyId = requireCompanyOrParam();
diff --git a/db.php b/db.php
index 8b4262c..410c02c 100644
--- a/db.php
+++ b/db.php
@@ -346,6 +346,18 @@ function initDatabase(): void {
INDEX idx_company (company_id)
) 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 (
id INT AUTO_INCREMENT PRIMARY KEY,
company_id VARCHAR(50) NOT NULL,
@@ -1582,6 +1594,52 @@ function dbDeleteTicketRule(string $ruleId): void {
_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 ====================
function dbGetCompanyConfig(string $companyId): array {
diff --git a/index.html b/index.html
index 6731f85..0f27d2b 100644
--- a/index.html
+++ b/index.html
@@ -1066,6 +1066,9 @@
+
+
+
-
-
-
-
-
-
Automaattisäännöt
-
-
-
Säännöt soveltuvat automaattisesti uusiin tiketteihin haettaessa sähköposteja. Ensimmäinen täsmäävä sääntö voittaa.
-
-
-
-
-
-
-
-
-
-
-
-
-
Vastauspohjat
-
-
-
Yrityksen yhteiset vastauspohjat tiketteihin. Valittavissa vastauslomakkeen valikosta kaikille käyttäjille.
-
-
-
-
-
-
-
-
-
-
-
Omat asetukset
-
-
-
-
Sähköpostiallekirjoitukset
-
Allekirjoitus liitetään automaattisesti sähköpostivastausten loppuun.
-
-
-
-
-
Postilaatikoiden näkyvyys
-
Poista rasti postilaatikoista joiden tikettejä et halua nähdä.
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
Automaattisäännöt
+
+
+
Säännöt soveltuvat automaattisesti uusiin tiketteihin haettaessa sähköposteja. Ensimmäinen täsmäävä sääntö voittaa.
+
+
+
+
+
+
+
+
Tikettityypit
+
Hallitse yrityksen tikettityyppejä. Käytössä olevia tyyppejä ei voi poistaa.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Vastauspohjat
+
+
+
Yrityksen yhteiset vastauspohjat tiketteihin. Valittavissa vastauslomakkeen valikosta kaikille käyttäjille.
+
+
+
+
+
+
+
+
+
+
+
+
Sähköpostiallekirjoitukset
+
Allekirjoitus liitetään automaattisesti sähköpostivastausten loppuun.
+
+
+
+
+
Postilaatikoiden näkyvyys
+
Poista rasti postilaatikoista joiden tikettejä et halua nähdä.
+
+
+
+
+
+
+
+
+
diff --git a/script.js b/script.js
index 7b58eb1..ae1c080 100644
--- a/script.js
+++ b/script.js
@@ -301,11 +301,8 @@ function switchToTab(target, subTab) {
if (target === 'support') {
loadTickets(); showTicketListView();
if (document.getElementById('ticket-auto-refresh').checked) startTicketAutoRefresh();
- if (subTab === 'ohjeet') {
- switchSupportSubTab('support-ohjeet');
- } else {
- switchSupportSubTab('support-tickets');
- }
+ const supportSubMap = { ohjeet: 'support-ohjeet', saannot: 'support-saannot', vastauspohjat: 'support-vastauspohjat', asetukset: 'support-asetukset' };
+ switchSupportSubTab(supportSubMap[subTab] || 'support-tickets');
}
if (target === 'documents') {
if (subTab && subTab !== 'kokoukset') {
@@ -1344,7 +1341,7 @@ const ticketStatusLabels = {
suljettu: 'Suljettu',
};
-const ticketTypeLabels = {
+let ticketTypeLabels = {
laskutus: 'Laskutus',
tekniikka: 'Tekniikka',
vika: 'Vika',
@@ -1520,10 +1517,9 @@ async function showTicketDetail(id, companyId = '') {