Files
breadcrumb-the-shire/modules/helpdesk/templates/aside-helpdesk-panel.phtml
fs 5a4974909b feat(helpdesk): add Risk Radar dashboard with customer risk scoring
New portfolio view scoring customers 0-100 across five dimensions:
- Open pressure (30%): open + critical ticket count
- Trend (25%): net ticket flow (created - closed) in period
- SLA overdue (20%): tickets exceeding escalation targets
- Resolution time (15%): median hours to close (null = neutral)
- Inactivity (10%): age of oldest open ticket

Card grid with score badges (color-coded high/medium/low), metric
pills, driver bars. Click opens detail dialog with all dimensions
and open ticket list. Clientside search filter.

PBI_LV_Tickets with client-driven paging ($skip/$top, hardcap 5000).
Escalation definitions cached separately (30min TTL). Truncated
banner when data is capped.

New: RiskRadarService, getTicketsForRiskRadar(), 13 PHPUnit tests,
permission helpdesk.risk-radar.view, 2 routes, i18n de+en.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:24:48 +02:00

77 lines
2.7 KiB
PHTML

<?php
$layoutNav = is_array($layoutNav ?? null) ? $layoutNav : [];
$helpdeskNav = is_array($layoutNav['helpdesk.nav'] ?? null) ? $layoutNav['helpdesk.nav'] : [];
$canManageSettings = !empty($helpdeskNav['can_manage_settings']);
$canViewTeam = !empty($helpdeskNav['can_view_team']);
$canViewRiskRadar = !empty($helpdeskNav['can_view_risk_radar']);
$settingsActive = navActive('helpdesk/settings', true);
$teamActive = navActive('helpdesk/team', true);
$riskRadarActive = navActive('helpdesk/risk-radar', true);
$customersActive = navActive('helpdesk', true);
if ($settingsActive['isActive'] || $teamActive['isActive'] || $riskRadarActive['isActive']) {
$customersActive = ['class' => '', 'aria' => '', 'isActive' => false];
}
$helpdeskNavItems = [
[
'label' => t('Customers'),
'path' => 'helpdesk',
'active' => $customersActive,
'visible' => true,
],
[
'label' => t('Team dashboard'),
'path' => 'helpdesk/team',
'active' => $teamActive,
'visible' => $canViewTeam,
],
[
'label' => t('Risk radar'),
'path' => 'helpdesk/risk-radar',
'active' => $riskRadarActive,
'visible' => $canViewRiskRadar,
],
[
'label' => t('Settings'),
'path' => 'helpdesk/settings',
'active' => $settingsActive,
'visible' => $canManageSettings,
],
];
$visibleItems = array_values(array_filter($helpdeskNavItems, static fn (array $item): bool => !empty($item['visible'])));
$groupIsActive = false;
foreach ($visibleItems as $item) {
$activeState = $item['active'] ?? [];
if (!empty($activeState['isActive'])) {
$groupIsActive = true;
break;
}
}
?>
<ul class="app-sidebar-admin-nav" aria-label="<?php e(t('Helpdesk')); ?>">
<?php if ($visibleItems): ?>
<li class="app-sidebar-group app-sidebar-admin-group">
<details data-details-key="helpdesk-navigation"<?php if ($groupIsActive): ?> open<?php endif; ?>>
<summary>
<i class="bi bi-headset"></i>
<span><?php e(t('Helpdesk')); ?></span>
</summary>
<ul>
<?php foreach ($visibleItems as $item):
$active = $item['active'] ?? ['class' => '', 'aria' => ''];
?>
<li>
<a href="<?php e(lurl($item['path'])); ?>" class="<?php e($active['class'] ?? ''); ?>" <?php echo $active['aria'] ?? ''; ?>>
<?php e($item['label']); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</details>
</li>
<?php endif; ?>
</ul>