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);
}
?>