Add auto-rules management, bulk actions for tickets

- Auto-rules JS: load, render, save, edit, delete, toggle rules
- Rules UI: Säännöt view with rule list and form
- Bulk actions: checkbox selection in ticket list
- Bulk close/delete endpoints (ticket_bulk_status, ticket_bulk_delete)
- Bulk toolbar with select all, close selected, delete selected

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 10:18:10 +02:00
parent ea7c3e0cf7
commit 7fcf0a3b31
3 changed files with 377 additions and 0 deletions

128
api.php
View File

@@ -1534,12 +1534,40 @@ switch ($action) {
'from_email' => $email['from_email'],
'from_name' => $email['from_name'],
'status' => 'uusi',
'type' => 'muu',
'assigned_to' => '',
'customer_id' => '',
'customer_name' => '',
'created' => $email['date'],
'updated' => $email['date'],
'message_id' => $email['message_id'],
'messages' => [$msg],
];
// Apply auto-rules
$rules = $config['ticket_rules'] ?? [];
foreach ($rules as $rule) {
if (empty($rule['enabled'])) continue;
$match = true;
if (!empty($rule['from_contains'])) {
$needle = strtolower($rule['from_contains']);
if (strpos(strtolower($email['from_email'] . ' ' . $email['from_name']), $needle) === false) {
$match = false;
}
}
if (!empty($rule['subject_contains'])) {
$needle = strtolower($rule['subject_contains']);
if (strpos(strtolower($email['subject'] ?? ''), $needle) === false) {
$match = false;
}
}
if ($match) {
if (!empty($rule['set_status'])) $ticket['status'] = $rule['set_status'];
if (!empty($rule['set_type'])) $ticket['type'] = $rule['set_type'];
break; // First matching rule wins
}
}
$tickets[] = $ticket;
$newCount++;
}
@@ -1802,6 +1830,106 @@ switch ($action) {
echo json_encode(['success' => true]);
break;
case 'ticket_rules':
requireAuth();
$config = loadConfig();
echo json_encode($config['ticket_rules'] ?? []);
break;
case 'ticket_rule_save':
requireAuth();
if ($method !== 'POST') break;
$input = json_decode(file_get_contents('php://input'), true);
$config = loadConfig();
$rules = $config['ticket_rules'] ?? [];
$rule = [
'id' => $input['id'] ?? generateId(),
'name' => trim($input['name'] ?? ''),
'from_contains' => trim($input['from_contains'] ?? ''),
'subject_contains' => trim($input['subject_contains'] ?? ''),
'set_status' => $input['set_status'] ?? '',
'set_type' => $input['set_type'] ?? '',
'enabled' => $input['enabled'] ?? true,
];
if (empty($rule['name'])) {
http_response_code(400);
echo json_encode(['error' => 'Säännön nimi puuttuu']);
break;
}
// Update existing or add new
$found = false;
foreach ($rules as &$r) {
if ($r['id'] === $rule['id']) {
$r = $rule;
$found = true;
break;
}
}
unset($r);
if (!$found) $rules[] = $rule;
$config['ticket_rules'] = $rules;
file_put_contents(DATA_DIR . '/config.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
addLog('config_update', '', '', 'Tikettisääntö: ' . $rule['name']);
echo json_encode($rule);
break;
case 'ticket_bulk_status':
requireAuth();
if ($method !== 'POST') break;
$input = json_decode(file_get_contents('php://input'), true);
$ids = $input['ids'] ?? [];
$newStatus = $input['status'] ?? '';
$validStatuses = ['uusi','kasittelyssa','odottaa','ratkaistu','suljettu'];
if (!in_array($newStatus, $validStatuses)) {
http_response_code(400);
echo json_encode(['error' => 'Virheellinen tila']);
break;
}
$tickets = json_decode(file_get_contents(TICKETS_FILE), true) ?: [];
$changed = 0;
foreach ($tickets as &$t) {
if (in_array($t['id'], $ids)) {
$t['status'] = $newStatus;
$t['updated'] = date('Y-m-d H:i:s');
$changed++;
}
}
unset($t);
file_put_contents(TICKETS_FILE, json_encode($tickets, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
addLog('ticket_status', '', '', "Massapäivitys: $changed tikettiä → $newStatus");
echo json_encode(['success' => true, 'changed' => $changed]);
break;
case 'ticket_bulk_delete':
requireAuth();
if ($method !== 'POST') break;
$input = json_decode(file_get_contents('php://input'), true);
$ids = $input['ids'] ?? [];
$tickets = json_decode(file_get_contents(TICKETS_FILE), true) ?: [];
$before = count($tickets);
$tickets = array_values(array_filter($tickets, fn($t) => !in_array($t['id'], $ids)));
$deleted = $before - count($tickets);
file_put_contents(TICKETS_FILE, json_encode($tickets, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
addLog('ticket_delete', '', '', "Massapoisto: $deleted tikettiä");
echo json_encode(['success' => true, 'deleted' => $deleted]);
break;
case 'ticket_rule_delete':
requireAuth();
if ($method !== 'POST') break;
$input = json_decode(file_get_contents('php://input'), true);
$ruleId = $input['id'] ?? '';
$config = loadConfig();
$rules = $config['ticket_rules'] ?? [];
$config['ticket_rules'] = array_values(array_filter($rules, fn($r) => $r['id'] !== $ruleId));
file_put_contents(DATA_DIR . '/config.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
echo json_encode(['success' => true]);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Tuntematon toiminto']);