feat(helpdesk): replace abstract driver bars with concrete reason texts

Cards now show plain-language reasons instead of abstract progress bars:
'8 open, 3 critical', '↑4 more than resolved', '2 SLA breaches',
'Oldest ticket 12d'. Instantly understandable without knowing the
scoring formula. Driver bars remain in the detail dialog for the
full dimension breakdown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 00:36:49 +02:00
parent 53db166916
commit e279e469da
5 changed files with 40 additions and 31 deletions

View File

@@ -323,5 +323,8 @@
"open": "offen",
"overdue": "überfällig",
"net": "netto",
"Search customers...": "Kunden suchen..."
"Search customers...": "Kunden suchen...",
"more than resolved": "mehr als gelöst",
"SLA breaches": "SLA-Verletzungen",
"Oldest ticket": "Ältestes Ticket"
}

View File

@@ -323,5 +323,8 @@
"open": "open",
"overdue": "overdue",
"net": "net",
"Search customers...": "Search customers..."
"Search customers...": "Search customers...",
"more than resolved": "more than resolved",
"SLA breaches": "SLA breaches",
"Oldest ticket": "Oldest ticket"
}

View File

@@ -36,6 +36,9 @@ $periodLabels = [
data-label-ticket="<?php e(t('Ticket')); ?>"
data-label-status="<?php e(t('Status')); ?>"
data-label-age="<?php e(t('Age')); ?>"
data-label-more-than-resolved="<?php e(t('more than resolved')); ?>"
data-label-sla-breaches="<?php e(t('SLA breaches')); ?>"
data-label-oldest-ticket="<?php e(t('Oldest ticket')); ?>"
data-label-tickets="<?php e(t('Tickets')); ?>"
data-label-score="<?php e(t('Risk score')); ?>"
>

View File

@@ -630,22 +630,15 @@
font-weight: 600;
}
.helpdesk-risk-card-drivers {
display: grid;
gap: calc(var(--app-spacing) * 0.2);
}
.helpdesk-risk-card-driver {
display: flex;
align-items: center;
gap: calc(var(--app-spacing) * 0.35);
.helpdesk-risk-card-reasons {
list-style: none;
margin: 0;
padding: 0;
font-size: var(--text-xs, 0.75rem);
}
.helpdesk-risk-card-driver-label {
min-width: 7ch;
color: var(--app-muted-color);
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 0.15em;
}
.helpdesk-risk-card-driver-bar {

View File

@@ -111,22 +111,29 @@ if (container) {
card.appendChild(metrics);
// Top drivers
const drivers = customer.drivers || [];
if (drivers.length > 0) {
const driverSection = h('div', 'helpdesk-risk-card-drivers');
for (const dr of drivers) {
if (dr.dimension_score === null || dr.weighted_points <= 0) continue;
const row = h('div', 'helpdesk-risk-card-driver');
row.appendChild(h('span', 'helpdesk-risk-card-driver-label', dimLabel(dr.id)));
const bar = h('div', 'helpdesk-risk-card-driver-bar');
const fill = h('div', 'helpdesk-risk-card-driver-fill');
fill.style.width = Math.min(100, dr.dimension_score) + '%';
bar.appendChild(fill);
row.appendChild(bar);
driverSection.appendChild(row);
// Why at risk — concrete facts
const kpiOpen = kpis.open || 0;
const kpiCritical = kpis.critical || 0;
const kpiSla = kpis.sla_overdue || 0;
const kpiNet = kpis.net_flow || 0;
const kpiOldest = kpis.oldest_open_hours || 0;
const reasons = [];
if (kpiOpen > 0) {
let text = kpiOpen + ' ' + d('labelOpen', 'open');
if (kpiCritical > 0) text += ', ' + kpiCritical + ' ' + d('labelCritical', 'critical');
reasons.push(text);
}
card.appendChild(driverSection);
if (kpiNet > 0) reasons.push('↑' + kpiNet + ' ' + d('labelMoreThanResolved', 'more than resolved'));
if (kpiSla > 0) reasons.push(kpiSla + ' ' + d('labelSlaBreaches', 'SLA breaches'));
if (kpiOldest > 168) reasons.push(d('labelOldestTicket', 'Oldest ticket') + ' ' + formatAge(kpiOldest));
if (reasons.length > 0) {
const reasonList = h('ul', 'helpdesk-risk-card-reasons');
for (const r of reasons) {
reasonList.appendChild(h('li', '', '• ' + r));
}
card.appendChild(reasonList);
}
// Click/keyboard to open detail