Add ticket tags system, tag filtering, and auto-close feature
- Fix tickets API endpoint: add type, customer_name, customer_id, tags fields - Add tags array to ticket data structure with add/remove UI - Add tag filter input to toolbar and tag column in ticket list - Add ticket_tags API endpoint for updating tags - Add set_tags and auto_close_days actions to auto-rules - Auto-close check runs on ticket list load, closes expired tickets - Add tag CSS styles with editable tag badges in detail view Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
66
api.php
66
api.php
@@ -1430,6 +1430,22 @@ switch ($action) {
|
||||
requireAuth();
|
||||
$tickets = loadTickets();
|
||||
// Palauta ilman viestisisältöjä (lista-näkymä)
|
||||
// Auto-close tarkistus: sulje tiketit joiden auto_close_at on ohitettu
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$autoCloseCount = 0;
|
||||
foreach ($tickets as &$tc) {
|
||||
if (!empty($tc['auto_close_at']) && $tc['auto_close_at'] <= $now && !in_array($tc['status'], ['suljettu'])) {
|
||||
$tc['status'] = 'suljettu';
|
||||
$tc['updated'] = $now;
|
||||
$autoCloseCount++;
|
||||
}
|
||||
}
|
||||
unset($tc);
|
||||
if ($autoCloseCount > 0) {
|
||||
saveTickets($tickets);
|
||||
addLog('ticket_auto_close', '', '', "Automaattisulku: $autoCloseCount tikettiä");
|
||||
}
|
||||
|
||||
$list = array_map(function($t) {
|
||||
$msgCount = count($t['messages'] ?? []);
|
||||
$lastMsg = $msgCount > 0 ? $t['messages'][$msgCount - 1] : null;
|
||||
@@ -1439,7 +1455,12 @@ switch ($action) {
|
||||
'from_email' => $t['from_email'],
|
||||
'from_name' => $t['from_name'],
|
||||
'status' => $t['status'],
|
||||
'type' => $t['type'] ?? 'muu',
|
||||
'assigned_to' => $t['assigned_to'] ?? '',
|
||||
'customer_id' => $t['customer_id'] ?? '',
|
||||
'customer_name' => $t['customer_name'] ?? '',
|
||||
'tags' => $t['tags'] ?? [],
|
||||
'auto_close_at' => $t['auto_close_at'] ?? '',
|
||||
'created' => $t['created'],
|
||||
'updated' => $t['updated'],
|
||||
'message_count' => $msgCount,
|
||||
@@ -1538,6 +1559,8 @@ switch ($action) {
|
||||
'assigned_to' => '',
|
||||
'customer_id' => '',
|
||||
'customer_name' => '',
|
||||
'tags' => [],
|
||||
'auto_close_at' => '',
|
||||
'created' => $email['date'],
|
||||
'updated' => $email['date'],
|
||||
'message_id' => $email['message_id'],
|
||||
@@ -1564,6 +1587,16 @@ switch ($action) {
|
||||
if ($match) {
|
||||
if (!empty($rule['set_status'])) $ticket['status'] = $rule['set_status'];
|
||||
if (!empty($rule['set_type'])) $ticket['type'] = $rule['set_type'];
|
||||
if (!empty($rule['set_tags'])) {
|
||||
$ruleTags = array_map('trim', explode(',', $rule['set_tags']));
|
||||
$ticket['tags'] = array_values(array_unique(array_merge($ticket['tags'], $ruleTags)));
|
||||
}
|
||||
if (!empty($rule['auto_close_days'])) {
|
||||
$days = intval($rule['auto_close_days']);
|
||||
if ($days > 0) {
|
||||
$ticket['auto_close_at'] = date('Y-m-d H:i:s', strtotime("+{$days} days"));
|
||||
}
|
||||
}
|
||||
break; // First matching rule wins
|
||||
}
|
||||
}
|
||||
@@ -1830,6 +1863,37 @@ switch ($action) {
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
|
||||
case 'ticket_tags':
|
||||
requireAuth();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$id = $input['id'] ?? '';
|
||||
$tags = $input['tags'] ?? [];
|
||||
// Sanitize tags: trim, lowercase, remove empty
|
||||
$tags = array_values(array_filter(array_map(function($t) {
|
||||
return trim(strtolower($t));
|
||||
}, $tags)));
|
||||
$tickets = loadTickets();
|
||||
$found = false;
|
||||
foreach ($tickets as &$t) {
|
||||
if ($t['id'] === $id) {
|
||||
$t['tags'] = $tags;
|
||||
$t['updated'] = date('Y-m-d H:i:s');
|
||||
$found = true;
|
||||
addLog('ticket_tags', $t['id'], $t['subject'], 'Tagit: ' . implode(', ', $tags));
|
||||
echo json_encode($t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
unset($t);
|
||||
if (!$found) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Tikettiä ei löydy']);
|
||||
break;
|
||||
}
|
||||
saveTickets($tickets);
|
||||
break;
|
||||
|
||||
case 'ticket_rules':
|
||||
requireAuth();
|
||||
$config = loadConfig();
|
||||
@@ -1850,6 +1914,8 @@ switch ($action) {
|
||||
'subject_contains' => trim($input['subject_contains'] ?? ''),
|
||||
'set_status' => $input['set_status'] ?? '',
|
||||
'set_type' => $input['set_type'] ?? '',
|
||||
'set_tags' => trim($input['set_tags'] ?? ''),
|
||||
'auto_close_days' => intval($input['auto_close_days'] ?? 0),
|
||||
'enabled' => $input['enabled'] ?? true,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user