feat(helpdesk): redesign Historical tab with per-agent performance widgets
Each agent gets a card with three insight columns: - Resolution time: Ø / Min / Max (formatted as hours or days) - Top 3 customers by resolved ticket count (ranked list) - Top 3 categories by resolved ticket count (ranked list) Backend collects resolved ticket details per agent: customer names, categories, and resolution hours. Aggregates top-3 and min/max/avg. Replaces the old compact single-row layout with rich widget cards matching the Current tab pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1620,13 +1620,29 @@ class DebitorDetailService
|
||||
'age_hours_sum' => 0,
|
||||
'resolved_in_period' => 0,
|
||||
'open_ticket_details' => [],
|
||||
'resolved_customers' => [],
|
||||
'resolved_categories' => [],
|
||||
'resolution_hours' => [],
|
||||
];
|
||||
}
|
||||
|
||||
if ($isClosed) {
|
||||
$createdTs = self::parseIsoToTimestamp((string) ($ticket['Created_On'] ?? ''));
|
||||
if ($activityTs !== null && $activityTs >= $periodStart) {
|
||||
$agents[$normalizedUser]['resolved_in_period']++;
|
||||
$globalResolved++;
|
||||
|
||||
$customerName = trim((string) ($ticket['Company_Contact_Name'] ?? (string) ($ticket['Cust_Name'] ?? '')));
|
||||
$category = trim((string) ($ticket['Category_1_Description'] ?? (string) ($ticket['Category_1_Code'] ?? '')));
|
||||
if ($customerName !== '') {
|
||||
$agents[$normalizedUser]['resolved_customers'][$customerName] = ($agents[$normalizedUser]['resolved_customers'][$customerName] ?? 0) + 1;
|
||||
}
|
||||
if ($category !== '') {
|
||||
$agents[$normalizedUser]['resolved_categories'][$category] = ($agents[$normalizedUser]['resolved_categories'][$category] ?? 0) + 1;
|
||||
}
|
||||
if ($createdTs !== null && $activityTs > $createdTs) {
|
||||
$agents[$normalizedUser]['resolution_hours'][] = intdiv($activityTs - $createdTs, 3600);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$agents[$normalizedUser]['open_tickets']++;
|
||||
@@ -1685,6 +1701,20 @@ class DebitorDetailService
|
||||
return $b['age_hours'] <=> $a['age_hours'];
|
||||
});
|
||||
|
||||
// Performance insights from resolved tickets
|
||||
$topCustomers = $agent['resolved_customers'];
|
||||
arsort($topCustomers);
|
||||
$topCustomers = array_slice($topCustomers, 0, 3, true);
|
||||
|
||||
$topCategories = $agent['resolved_categories'];
|
||||
arsort($topCategories);
|
||||
$topCategories = array_slice($topCategories, 0, 3, true);
|
||||
|
||||
$resHours = $agent['resolution_hours'];
|
||||
$avgResolution = $resHours !== [] ? (int) round(array_sum($resHours) / count($resHours)) : null;
|
||||
$minResolution = $resHours !== [] ? min($resHours) : null;
|
||||
$maxResolution = $resHours !== [] ? max($resHours) : null;
|
||||
|
||||
$members[] = [
|
||||
'support_user' => $agent['support_user'],
|
||||
'display_name' => $agent['display_name'],
|
||||
@@ -1693,6 +1723,21 @@ class DebitorDetailService
|
||||
'avg_age_hours' => $avgAge,
|
||||
'resolved_in_period' => $agent['resolved_in_period'],
|
||||
'open_ticket_details' => $details,
|
||||
'performance' => [
|
||||
'top_customers' => array_map(
|
||||
static fn (string $name, int $count): array => ['name' => $name, 'count' => $count],
|
||||
array_keys($topCustomers),
|
||||
array_values($topCustomers)
|
||||
),
|
||||
'top_categories' => array_map(
|
||||
static fn (string $name, int $count): array => ['name' => $name, 'count' => $count],
|
||||
array_keys($topCategories),
|
||||
array_values($topCategories)
|
||||
),
|
||||
'avg_resolution_hours' => $avgResolution,
|
||||
'min_resolution_hours' => $minResolution,
|
||||
'max_resolution_hours' => $maxResolution,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user