diff --git a/api/update_ticket.php b/api/update_ticket.php index baa9b71..592fb03 100644 --- a/api/update_ticket.php +++ b/api/update_ticket.php @@ -180,9 +180,20 @@ try { $this->ticketModel->updateVisibility($id, $data['visibility'], $visibilityGroups, $this->userId); } - // Log ticket update to audit log + // Log ticket update to audit log — only the changed fields (delta) if ($this->userId) { - $this->auditLog->logTicketUpdate($this->userId, $id, $data); + $trackFields = ['title', 'priority', 'status', 'description', 'category', 'type']; + $delta = []; + foreach ($trackFields as $field) { + $oldVal = (string)($currentTicket[$field] ?? ''); + $newVal = (string)($updateData[$field] ?? ''); + if ($oldVal !== $newVal) { + $delta[$field] = ['from' => $oldVal, 'to' => $newVal]; + } + } + if (!empty($delta)) { + $this->auditLog->logTicketUpdate($this->userId, $id, $delta); + } } return [ diff --git a/controllers/TicketController.php b/controllers/TicketController.php index 2d41e11..60eabc7 100644 --- a/controllers/TicketController.php +++ b/controllers/TicketController.php @@ -161,14 +161,28 @@ class TicketController { return; } + // Fetch current ticket values before updating (for delta logging) + $oldTicket = $this->ticketModel->getTicketById((int)$id); + // Update ticket with user tracking // Pass expected_updated_at for optimistic locking if provided $expectedUpdatedAt = $data['expected_updated_at'] ?? null; $result = $this->ticketModel->updateTicket($data, $userId, $expectedUpdatedAt); - // Log ticket update to audit log - if ($result['success'] && isset($GLOBALS['auditLog']) && $userId) { - $GLOBALS['auditLog']->logTicketUpdate($userId, $id, $data); + // Log ticket update to audit log — only the changed fields (delta) + if ($result['success'] && isset($GLOBALS['auditLog']) && $userId && $oldTicket) { + $trackFields = ['title', 'priority', 'status', 'description', 'category', 'type']; + $delta = []; + foreach ($trackFields as $field) { + $oldVal = (string)($oldTicket[$field] ?? ''); + $newVal = (string)($data[$field] ?? ''); + if ($oldVal !== $newVal) { + $delta[$field] = ['from' => $oldVal, 'to' => $newVal]; + } + } + if (!empty($delta)) { + $GLOBALS['auditLog']->logTicketUpdate($userId, $id, $delta); + } } // Return JSON response diff --git a/views/TicketView.php b/views/TicketView.php index c8e1b40..5c90566 100644 --- a/views/TicketView.php +++ b/views/TicketView.php @@ -569,11 +569,26 @@ include __DIR__ . '/layout_header.php'; if (is_array($det)) { $parts = []; foreach ($det as $k => $v) { - if ($k !== 'old_value' && $k !== 'new_value') { + // Delta format: { field: { from: '...', to: '...' } } + if (is_array($v) && isset($v['from'], $v['to'])) { + $label = ucfirst(str_replace('_', ' ', $k)); + // Truncate long values (e.g. description) + $from = mb_strlen((string)$v['from']) > 60 + ? mb_substr((string)$v['from'], 0, 60) . '…' + : (string)$v['from']; + $to = mb_strlen((string)$v['to']) > 60 + ? mb_substr((string)$v['to'], 0, 60) . '…' + : (string)$v['to']; + $parts[] = '' . htmlspecialchars($label) . ': ' + . '' . htmlspecialchars($from) . '' + . ' ' + . '' . htmlspecialchars($to) . ''; + } elseif ($k !== 'old_value' && $k !== 'new_value') { + // Legacy flat format fallback $parts[] = '' . htmlspecialchars($k) . ': ' . htmlspecialchars((string)$v); } } - echo implode(', ', $parts); + echo implode('
', $parts); } ?>