From f2aa45dd2c4527a87d12432ff3d51e5f6bbdcf97 Mon Sep 17 00:00:00 2001 From: fs Date: Sun, 5 Apr 2026 21:09:52 +0200 Subject: [PATCH] feat(helpdesk): add trend delta indicator per agent in team dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shows a small arrow next to the ticket count in each agent card header: ↑3 (orange) = received 3 more than resolved → backlog growing ↓2 (green) = resolved 2 more than received → backlog shrinking Hidden when delta is 0 (stable). Delta is computed from received_in_period (tickets created in the selected period assigned to this agent) minus resolved_in_period. No snapshot storage needed — derived from existing ticket data. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Helpdesk/Service/DebitorDetailService.php | 11 +++++++++++ modules/helpdesk/web/css/helpdesk.css | 16 ++++++++++++++++ modules/helpdesk/web/js/helpdesk-team.js | 9 +++++++++ 3 files changed, 36 insertions(+) diff --git a/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php b/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php index c058d29..d347392 100644 --- a/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php +++ b/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php @@ -1621,6 +1621,7 @@ class DebitorDetailService 'critical_tickets' => 0, 'age_hours_sum' => 0, 'resolved_in_period' => 0, + 'received_in_period' => 0, 'open_ticket_details' => [], 'resolved_customers' => [], 'resolved_categories' => [], @@ -1631,6 +1632,9 @@ class DebitorDetailService if ($isClosed) { $createdTs = self::parseIsoToTimestamp((string) ($ticket['Created_On'] ?? '')); + if ($createdTs !== null && $createdTs >= $periodStart) { + $agents[$normalizedUser]['received_in_period']++; + } if ($activityTs !== null && $activityTs >= $periodStart) { $agents[$normalizedUser]['resolved_in_period']++; $globalResolved++; @@ -1659,6 +1663,11 @@ class DebitorDetailService $agents[$normalizedUser]['open_tickets']++; $globalOpen++; + $createdTs = self::parseIsoToTimestamp((string) ($ticket['Created_On'] ?? '')); + if ($createdTs !== null && $createdTs >= $periodStart) { + $agents[$normalizedUser]['received_in_period']++; + } + if ($rawUser === '') { $globalUnassigned++; } @@ -1733,6 +1742,8 @@ class DebitorDetailService 'critical_tickets' => $agent['critical_tickets'], 'avg_age_hours' => $avgAge, 'resolved_in_period' => $agent['resolved_in_period'], + 'received_in_period' => $agent['received_in_period'], + 'trend_delta' => $agent['received_in_period'] - $agent['resolved_in_period'], 'open_ticket_details' => $details, 'performance' => [ 'top_customers' => array_map( diff --git a/modules/helpdesk/web/css/helpdesk.css b/modules/helpdesk/web/css/helpdesk.css index e8ebd79..c70f923 100644 --- a/modules/helpdesk/web/css/helpdesk.css +++ b/modules/helpdesk/web/css/helpdesk.css @@ -220,6 +220,22 @@ border-radius: 0; } + /* Team workload — trend delta indicator */ + .helpdesk-team-trend-up, + .helpdesk-team-trend-down { + font-size: var(--text-xs, 0.75rem); + font-weight: 600; + font-variant-numeric: tabular-nums; + } + + .helpdesk-team-trend-up { + color: var(--app-warning, #f59e0b); + } + + .helpdesk-team-trend-down { + color: var(--app-success, #198754); + } + /* Team workload — inline avatar for section titles */ .helpdesk-team-avatar-inline { display: inline-flex; diff --git a/modules/helpdesk/web/js/helpdesk-team.js b/modules/helpdesk/web/js/helpdesk-team.js index b869dc2..039c057 100644 --- a/modules/helpdesk/web/js/helpdesk-team.js +++ b/modules/helpdesk/web/js/helpdesk-team.js @@ -110,6 +110,15 @@ if (container) { title.appendChild(document.createTextNode(name)); title.appendChild(h('span', 'helpdesk-team-title-count', count)); + + // Trend delta: ↑3 (growing) or ↓2 (shrinking) + const delta = m.trend_delta ?? 0; + if (delta !== 0) { + const arrow = delta > 0 ? '↑' : '↓'; + const cls = delta > 0 ? 'helpdesk-team-trend-up' : 'helpdesk-team-trend-down'; + title.appendChild(h('span', cls, arrow + Math.abs(delta))); + } + section.appendChild(title); // Ticket table