feat(helpdesk): enrich team ticket table with links, customer name, description, last activity
- Switch gateway from PBI_LV_Tickets to PBI_FP_Tickets to get Description and Company_Contact_Name fields - Ticket number links to ticket detail page - Customer column shows Company_Contact_Name with link to debitor detail - Description column with text-overflow ellipsis - Last activity column shows formatted date (was: age in hours) - Backend provides last_activity ISO string and customer_name per ticket - Table headers use i18n labels via data attributes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -597,9 +597,9 @@ class BcODataGateway
|
||||
*/
|
||||
public function getTicketsForTeam(): array
|
||||
{
|
||||
$url = $this->settingsGateway->buildEntityUrl(self::ENTITY_TICKETS_LV)
|
||||
$url = $this->settingsGateway->buildEntityUrl(self::ENTITY_TICKETS)
|
||||
. '?$top=500'
|
||||
. '&$select=' . rawurlencode('No,Customer_No,Category_1_Code,Escalation_Code,Ticket_State,Last_Activity_Date,Created_On,Process_Stage,Process_Code,Support_User_Name')
|
||||
. '&$select=' . rawurlencode('No,Description,Company_Contact_Name,Ticket_State,Support_User_Name,Created_On,Last_Activity_Date')
|
||||
. '&$orderby=' . rawurlencode('Created_On desc');
|
||||
|
||||
$response = $this->request('GET', $url);
|
||||
|
||||
@@ -1651,12 +1651,18 @@ class DebitorDetailService
|
||||
$globalOpenCount++;
|
||||
}
|
||||
|
||||
$lastActivityRaw = trim((string) ($ticket['Last_Activity_Date'] ?? ''));
|
||||
$lastActivityDisplay = ($lastActivityRaw !== '' && !str_starts_with($lastActivityRaw, '0001'))
|
||||
? $lastActivityRaw
|
||||
: trim((string) ($ticket['Created_On'] ?? ''));
|
||||
|
||||
$agents[$normalizedUser]['open_ticket_details'][] = [
|
||||
'no' => trim((string) ($ticket['No'] ?? '')),
|
||||
'description' => trim((string) ($ticket['Description'] ?? '')),
|
||||
'age_hours' => $ageHours,
|
||||
'critical' => $isCritical,
|
||||
'customer' => trim((string) ($ticket['Customer_No'] ?? '')),
|
||||
'customer_name' => trim((string) ($ticket['Company_Contact_Name'] ?? '')),
|
||||
'last_activity' => $lastActivityDisplay,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ $periodLabels = [
|
||||
data-label-error="<?php e(t('Could not load team dashboard.')); ?>"
|
||||
data-label-empty="<?php e(t('No team data available.')); ?>"
|
||||
data-label-critical="<?php e(t('Critical (>48h open)')); ?>"
|
||||
data-ticket-base-url="<?php e(lurl('helpdesk/ticket/')); ?>"
|
||||
data-debitor-base-url="<?php e(lurl('helpdesk/debitor/')); ?>"
|
||||
data-label-ticket="<?php e(t('Ticket')); ?>"
|
||||
data-label-customer="<?php e(t('Debtor')); ?>"
|
||||
data-label-description="<?php e(t('Description')); ?>"
|
||||
data-label-last-activity="<?php e(t('Last activity')); ?>"
|
||||
>
|
||||
<section>
|
||||
<?php
|
||||
|
||||
@@ -202,10 +202,36 @@
|
||||
.helpdesk-team-ticket-no {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.helpdesk-team-ticket-no a,
|
||||
.helpdesk-team-ticket-table td a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.helpdesk-team-ticket-no a:hover,
|
||||
.helpdesk-team-ticket-table td a:hover {
|
||||
color: var(--app-primary, #0d6efd);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.helpdesk-team-ticket-desc {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 20ch;
|
||||
}
|
||||
|
||||
.helpdesk-team-ticket-date {
|
||||
white-space: nowrap;
|
||||
color: var(--app-muted-color);
|
||||
}
|
||||
|
||||
/* Critical: only highlight the ticket number, not the entire row */
|
||||
.helpdesk-team-ticket-critical .helpdesk-team-ticket-no {
|
||||
.helpdesk-team-ticket-critical .helpdesk-team-ticket-no,
|
||||
.helpdesk-team-ticket-critical .helpdesk-team-ticket-no a {
|
||||
color: var(--app-warning, #f59e0b);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ if (container) {
|
||||
const labelQueue = container.dataset.labelQueue || 'Queue';
|
||||
const labelTickets = container.dataset.labelTickets || 'Tickets';
|
||||
const labelCritical = container.dataset.labelCritical || 'Critical (>48h open)';
|
||||
const ticketBaseUrl = container.dataset.ticketBaseUrl || '';
|
||||
const debitorBaseUrl = container.dataset.debitorBaseUrl || '';
|
||||
|
||||
const elLoading = document.getElementById('team-loading');
|
||||
const elError = document.getElementById('team-error');
|
||||
@@ -55,18 +57,46 @@ if (container) {
|
||||
return d.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
}
|
||||
|
||||
function formatDateTime(isoString) {
|
||||
if (!isoString) return '—';
|
||||
const d = new Date(isoString);
|
||||
if (isNaN(d.getTime())) return '—';
|
||||
return d.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a ticket table row: No | Customer | Last activity | Age
|
||||
* Build a ticket table row: No (link) | Customer (link) | Description | Last activity
|
||||
*/
|
||||
function buildTicketRow(t) {
|
||||
const tr = h('tr', t.critical ? 'helpdesk-team-ticket-critical' : '');
|
||||
|
||||
const noCell = h('td', 'helpdesk-team-ticket-no', t.no);
|
||||
// Ticket number as link
|
||||
const noCell = h('td', 'helpdesk-team-ticket-no');
|
||||
if (ticketBaseUrl && t.no) {
|
||||
const a = h('a', '', t.no);
|
||||
a.href = ticketBaseUrl + encodeURIComponent(t.no);
|
||||
noCell.appendChild(a);
|
||||
} else {
|
||||
noCell.textContent = t.no || '—';
|
||||
}
|
||||
tr.appendChild(noCell);
|
||||
tr.appendChild(h('td', '', t.customer || '—'));
|
||||
tr.appendChild(h('td', '', t.description || '—'));
|
||||
const ageCell = h('td', '', formatAge(t.age_hours));
|
||||
tr.appendChild(ageCell);
|
||||
|
||||
// Customer name as link to debitor
|
||||
const customerCell = h('td');
|
||||
if (debitorBaseUrl && t.customer_name) {
|
||||
const a = h('a', '', t.customer_name);
|
||||
a.href = debitorBaseUrl + encodeURIComponent(t.customer_name);
|
||||
customerCell.appendChild(a);
|
||||
} else {
|
||||
customerCell.textContent = t.customer_name || '—';
|
||||
}
|
||||
tr.appendChild(customerCell);
|
||||
|
||||
// Description
|
||||
tr.appendChild(h('td', 'helpdesk-team-ticket-desc', t.description || '—'));
|
||||
|
||||
// Last activity date
|
||||
tr.appendChild(h('td', 'helpdesk-team-ticket-date', formatDateTime(t.last_activity)));
|
||||
|
||||
return tr;
|
||||
}
|
||||
@@ -97,10 +127,10 @@ if (container) {
|
||||
const table = h('table', 'helpdesk-team-ticket-table');
|
||||
const thead = h('thead');
|
||||
const headRow = h('tr');
|
||||
headRow.appendChild(h('th', '', 'Ticket'));
|
||||
headRow.appendChild(h('th', '', 'Kunde'));
|
||||
headRow.appendChild(h('th', '', 'Beschreibung'));
|
||||
headRow.appendChild(h('th', '', 'Alter'));
|
||||
headRow.appendChild(h('th', '', container.dataset.labelTicket || 'Ticket'));
|
||||
headRow.appendChild(h('th', '', container.dataset.labelCustomer || 'Customer'));
|
||||
headRow.appendChild(h('th', '', container.dataset.labelDescription || 'Description'));
|
||||
headRow.appendChild(h('th', '', container.dataset.labelLastActivity || 'Last activity'));
|
||||
thead.appendChild(headRow);
|
||||
table.appendChild(thead);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user