Show only changed fields (delta) in ticket activity timeline
Before: entire ticket data was logged and shown in the activity tab.
After: compare old vs new values before saving; log only fields that
actually changed as { field: { from: '...', to: '...' } } pairs.
- TicketController.php: fetch old ticket before update, compute delta
- api/update_ticket.php: same fix for the API endpoint (currentTicket
already fetched for auth, reuse it for delta comparison)
- TicketView.php: render delta format as "Field: old → new" with color;
truncate long values (description) at 60 chars; keep legacy flat format
as fallback for older log entries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+13
-2
@@ -180,9 +180,20 @@ try {
|
|||||||
$this->ticketModel->updateVisibility($id, $data['visibility'], $visibilityGroups, $this->userId);
|
$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) {
|
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 [
|
return [
|
||||||
|
|||||||
@@ -161,14 +161,28 @@ class TicketController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch current ticket values before updating (for delta logging)
|
||||||
|
$oldTicket = $this->ticketModel->getTicketById((int)$id);
|
||||||
|
|
||||||
// Update ticket with user tracking
|
// Update ticket with user tracking
|
||||||
// Pass expected_updated_at for optimistic locking if provided
|
// Pass expected_updated_at for optimistic locking if provided
|
||||||
$expectedUpdatedAt = $data['expected_updated_at'] ?? null;
|
$expectedUpdatedAt = $data['expected_updated_at'] ?? null;
|
||||||
$result = $this->ticketModel->updateTicket($data, $userId, $expectedUpdatedAt);
|
$result = $this->ticketModel->updateTicket($data, $userId, $expectedUpdatedAt);
|
||||||
|
|
||||||
// Log ticket update to audit log
|
// Log ticket update to audit log — only the changed fields (delta)
|
||||||
if ($result['success'] && isset($GLOBALS['auditLog']) && $userId) {
|
if ($result['success'] && isset($GLOBALS['auditLog']) && $userId && $oldTicket) {
|
||||||
$GLOBALS['auditLog']->logTicketUpdate($userId, $id, $data);
|
$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
|
// Return JSON response
|
||||||
|
|||||||
+17
-2
@@ -569,11 +569,26 @@ include __DIR__ . '/layout_header.php';
|
|||||||
if (is_array($det)) {
|
if (is_array($det)) {
|
||||||
$parts = [];
|
$parts = [];
|
||||||
foreach ($det as $k => $v) {
|
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[] = '<strong>' . htmlspecialchars($label) . ':</strong> '
|
||||||
|
. '<span class="lt-text-muted">' . htmlspecialchars($from) . '</span>'
|
||||||
|
. ' <span class="lt-text-amber">→</span> '
|
||||||
|
. '<span class="lt-text-cyan">' . htmlspecialchars($to) . '</span>';
|
||||||
|
} elseif ($k !== 'old_value' && $k !== 'new_value') {
|
||||||
|
// Legacy flat format fallback
|
||||||
$parts[] = '<strong>' . htmlspecialchars($k) . ':</strong> ' . htmlspecialchars((string)$v);
|
$parts[] = '<strong>' . htmlspecialchars($k) . ':</strong> ' . htmlspecialchars((string)$v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo implode(', ', $parts);
|
echo implode('<br>', $parts);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user