0, 'us' => 0, 'late' => 0]; $totals = $empty; $by_agent = []; $by_client = []; foreach ($tickets as $ticket) { $a = intval($ticket['ticket_assigned_to']); $c = intval($ticket['ticket_client_id']); $by_agent[$a] = $by_agent[$a] ?? ['user_id' => $a, 'agent' => $a ? ($ticket['user_name'] ?? '') : '', 'avatar' => $ticket['user_avatar'] ?? ''] + $empty; $by_client[$c] = $by_client[$c] ?? ['client_id' => $c, 'client' => $ticket['client_name'] ?? '', 'abbr' => $ticket['client_abbreviation'] ?? ''] + $empty; foreach ([&$by_agent[$a], &$by_client[$c], &$totals] as &$group) { $group[$ticket['bucket']]++; $group['total']++; $group['us'] += $ticket['move'] === 'us' ? 1 : 0; $group['late'] += $ticket['late'] ? 1 : 0; } unset($group); } $sort = function ($a, $b) { return $b['old'] + $b['90'] <=> $a['old'] + $a['90'] ?: $b['total'] <=> $a['total']; }; uasort($by_agent, $sort); uasort($by_client, $sort); $count_cell = function ($key) { return function ($row) use ($key) { if (!$row[$key]) { return '–'; } $tone = in_array($key, ['90', 'old'], true) ? 'text-danger-emphasis fw-semibold' : ($key === '30' ? 'text-warning-emphasis fw-semibold' : ''); return '' . $row[$key] . ''; }; }; $bucket_columns = []; foreach (REPORT_AGE_BUCKETS as $key => [$label]) { $bucket_columns[$key] = ['label' => $label, 'type' => 'count', 'html' => $count_cell($key)]; } $tail_columns = [ 'total' => ['label' => 'Open', 'type' => 'count', 'class' => 'fw-semibold'], 'us' => ['label' => 'Waiting on us', 'type' => 'count', 'title' => 'The client spoke last, or nobody has replied yet'], 'late' => ['label' => 'Late', 'type' => 'count', 'title' => 'Past their SLA or their due date', 'html' => function ($row) { return $row['late'] ? '' . $row['late'] . '' : '–'; }], ]; $agent_columns = ['agent' => ['label' => 'Agent', 'csv' => function ($row) { return $row['user_id'] ? $row['agent'] : 'Unassigned'; }, 'html' => function ($row) use ($client_id) { $url = '/agent/tickets.php?' . http_build_query(array_filter(['client_id' => $client_id ?: null, 'assigned' => $row['user_id'] ?: 'unassigned'])); return '' . formatAssignee($row['user_id'], $row['agent'], $row['avatar'], true) . ''; }]] + $bucket_columns + $tail_columns; $client_columns = ['client' => ['label' => 'Client', 'csv' => function ($row) { return $row['client_id'] ? $row['client'] : 'No client'; }, 'html' => function ($row) { return reportClientCell($row['client_id'], $row['client'], $row['abbr'], 'tickets.php'); }]] + $bucket_columns + $tail_columns; $move_labels = ['us' => ['Waiting on us', 'danger', 'fa-reply'], 'client' => ['Waiting on client', 'secondary', 'fa-hourglass-half'], 'parked' => ['Parked', 'secondary', 'fa-pause']]; $ticket_columns = [ 'ticket' => ['label' => 'Ticket', 'class' => 'itflow-report-text', 'csv' => function ($row) { return ticketReference($row) . ' ' . $row['ticket_subject']; }, 'html' => function ($row) { return reportPriorityDot($row['ticket_priority']) . reportTicketLink($row); }], 'client_name' => ['label' => 'Client', 'html' => function ($row) { return reportClientCell($row['ticket_client_id'], $row['client_name'] ?? '', $row['client_abbreviation'] ?? ''); }], 'user_name' => ['label' => 'Agent', 'csv' => function ($row) { return $row['user_name'] ?: 'Unassigned'; }, 'html' => function ($row) { return formatAssignee($row['ticket_assigned_to'], $row['user_name'] ?? '', $row['user_avatar'] ?? '', true); }], 'ticket_status_name' => ['label' => 'Status', 'html' => function ($row) { return ticketStatusHtml($row['ticket_status_name'], $row['ticket_status_color'], 'detail'); }], 'move' => ['label' => 'Whose move', 'csv' => function ($row) use ($move_labels) { return $move_labels[$row['move']][0] ?? ''; }, 'html' => function ($row) use ($move_labels) { if (!isset($move_labels[$row['move']])) { return ''; } [$label, $tone, $icon] = $move_labels[$row['move']]; return '' . $label . ''; }], 'sla' => ['label' => 'SLA / due', 'sort' => false, 'csv' => function ($row) { return trim(($row['sla']['key'] !== 'none' ? $row['sla']['key'] : '') . ' ' . ($row['due'] ? 'due ' . $row['ticket_due_at'] : '')); }, 'html' => function ($row) { return '' . ticketSlaPill($row['sla']) . ticketDuePill($row['due']) . ''; }], 'age_days' => ['label' => 'Open for', 'type' => 'decimal', 'decimals' => 1, 'csv_label' => 'Days open', 'html' => function ($row) { $days = $row['age_days']; $tone = $days > 30 ? 'text-danger-emphasis fw-semibold' : ($days > 7 ? 'text-warning-emphasis fw-semibold' : ''); return '' . reportDuration($row['age_seconds']) . ''; }], 'last_activity' => ['label' => 'Last activity', 'type' => 'date', 'csv' => function ($row) { return $row['last_activity']; }, 'html' => function ($row) { return '' . escapeHtml(timeAgo($row['last_activity'])) . ''; }], ]; if (reportExporting('agents')) { reportCsv('Open Tickets by Agent', $agent_columns, array_values($by_agent), ['agent' => 'Total'] + $totals); } if (reportExporting('clients')) { reportCsv('Open Tickets by Client', $client_columns, array_values($by_client), ['client' => 'Total'] + $totals); } if (reportExporting()) { reportCsv('Open Tickets', $ticket_columns, $tickets); } require_once "includes/inc_report_layout.php"; $unassigned = $by_agent[0]['total'] ?? 0; $oldest = $tickets[0] ?? null; $bucket_tones = ['day' => 'success', '3' => 'info', '7' => 'accent', '30' => 'warning', '90' => 'orange', 'old' => 'danger']; $agent_choices = '
'; ?>