Files
breadcrumb-the-shire/modules/helpdesk/pages/helpdesk/ticket(default).phtml
fs eed1e848df feat(helpdesk): redesign ticket detail with communication as main content and metadata aside
Swap layout: communication feed becomes the primary main content area (full width),
ticket metadata moves to aside panel with 2 stacked KPI tiles (Status, Age) and
a Stripe-style key-value definition list (Description, Company link, Contact,
Category, Support, Debtor No.). Remove activity timeline entirely — communication
feed already includes activity fallback when SOAP is unavailable. Add skeleton
bubble loading for async communication. Simplify JS from 211 to 89 lines. Remove
unused ticketLogUrl data attribute and i18n keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 16:32:53 +02:00

172 lines
7.3 KiB
PHTML

<?php
/**
* @var string $ticketNo
* @var string $fromCustomerNo
* @var array|null $ticket
* @var bool $connectionError
* @var string $ticketCustomerNo
* @var string $ticketDescription
* @var string $ticketCommunicationUrl
*/
$ticketNo = $ticketNo ?? '';
$fromCustomerNo = $fromCustomerNo ?? '';
$ticket = is_array($ticket ?? null) ? $ticket : null;
$connectionError = $connectionError ?? false;
$ticketCustomerNo = $ticketCustomerNo ?? '';
$ticketDescription = $ticketDescription ?? '';
$ticketCommunicationUrl = $ticketCommunicationUrl ?? '';
$backUrl = $ticketCustomerNo !== '' ? lurl('helpdesk/debitor/' . rawurlencode($ticketCustomerNo)) : lurl('helpdesk');
?>
<div class="app-details-container"
data-ticket-no="<?php e($ticketNo); ?>"
data-ticket-communication-url="<?php e($ticketCommunicationUrl); ?>"
>
<section>
<?php
$breadcrumbs = [
['label' => t('Home'), 'path' => 'admin'],
['label' => t('Helpdesk'), 'path' => 'helpdesk'],
];
if ($ticketCustomerNo !== '') {
$breadcrumbs[] = ['label' => (string) ($ticket['Company_Contact_Name'] ?? $ticketCustomerNo), 'path' => 'helpdesk/debitor/' . rawurlencode($ticketCustomerNo)];
}
$breadcrumbs[] = ['label' => t('Ticket') . ' ' . $ticketNo];
require templatePath('partials/app-breadcrumb.phtml');
?>
<?php
$titlebar = [
'title' => t('Ticket') . ' ' . $ticketNo,
'backHref' => $backUrl,
'backTitle' => t('Back'),
'actions' => [],
];
require templatePath('partials/app-details-titlebar.phtml');
?>
<?php require templatePath('partials/app-flash.phtml'); ?>
<?php if ($connectionError): ?>
<div class="notice" data-variant="error" role="alert">
<?php e(t('Could not connect to Business Central.')); ?>
</div>
<?php elseif ($ticket !== null): ?>
<!-- Communication feed — main content, loaded async -->
<div class="helpdesk-ticket-detail-content">
<section>
<h3 class="helpdesk-support-section-title"><?php e(t('Ticket communication')); ?></h3>
<div id="ticket-communication-loading">
<?php for ($i = 0; $i < 6; $i++): ?>
<div class="helpdesk-skeleton helpdesk-skeleton-aside-bubble <?php e($i % 2 === 1 ? 'helpdesk-skeleton-aside-bubble-alt' : ($i % 3 === 0 ? 'helpdesk-skeleton-aside-bubble-short' : '')); ?>"></div>
<?php endfor; ?>
</div>
<div id="ticket-communication-content" hidden>
<div id="ticket-communication-feed"></div>
</div>
</section>
</div>
<?php endif; ?>
</section>
<?php if (!$connectionError && $ticket !== null): ?>
<?php
$createdRaw = (string) ($ticket['Created_On'] ?? '');
$createdFormatted = $createdRaw !== '' && $createdRaw !== '0001-01-01T00:00:00Z'
? (new DateTimeImmutable($createdRaw))->format('d.m.Y H:i')
: '—';
$lastActivityRaw = (string) ($ticket['Last_Activity_Date'] ?? '');
$lastActivityFormatted = $lastActivityRaw !== '' && $lastActivityRaw !== '0001-01-01T00:00:00Z'
? (new DateTimeImmutable($lastActivityRaw))->format('d.m.Y H:i')
: '—';
$ticketState = (string) ($ticket['Ticket_State'] ?? '');
// Compute ticket age
$ageLabel = '—';
if ($createdRaw !== '' && $createdRaw !== '0001-01-01T00:00:00Z') {
$createdDt = new DateTimeImmutable($createdRaw);
$diff = $createdDt->diff(new DateTimeImmutable('now'));
if ($diff->days >= 1) {
$ageLabel = $diff->days . ' ' . ($diff->days === 1 ? t('day') : t('days'));
} else {
$hours = $diff->h + ($diff->days * 24);
$ageLabel = max(1, $hours) . ' ' . t('hours');
}
}
?>
<aside id="app-details-aside-section">
<div class="app-details-aside-section helpdesk-ticket-aside">
<!-- KPI tiles: Status + Age — stacked vertically -->
<h3 class="helpdesk-support-section-title"><?php e(t('Ticket details')); ?></h3>
<div class="helpdesk-support-metrics helpdesk-support-metrics-aside">
<article class="helpdesk-support-metric">
<p class="helpdesk-support-metric-label"><?php e(t('Status')); ?></p>
<p class="helpdesk-support-metric-value"><?php e($ticketState); ?></p>
<p class="helpdesk-support-metric-trend"><?php e(t('Last activity')); ?>: <?php e($lastActivityFormatted); ?></p>
</article>
<article class="helpdesk-support-metric">
<p class="helpdesk-support-metric-label"><?php e(t('Age')); ?></p>
<p class="helpdesk-support-metric-value"><?php e($ageLabel); ?></p>
<p class="helpdesk-support-metric-trend"><?php e(t('Created')); ?>: <?php e($createdFormatted); ?></p>
</article>
</div>
<!-- Compact key-value details -->
<h3 class="helpdesk-support-section-title"><?php e(t('Details')); ?></h3>
<dl class="helpdesk-ticket-meta">
<div class="helpdesk-ticket-meta-item">
<dt><?php e(t('Description')); ?></dt>
<dd><?php e((string) ($ticket['Description'] ?? '—')); ?></dd>
</div>
<div class="helpdesk-ticket-meta-item">
<dt><?php e(t('Company')); ?></dt>
<dd>
<?php if ($ticketCustomerNo !== ''): ?>
<a href="<?php e(lurl('helpdesk/debitor/' . rawurlencode($ticketCustomerNo))); ?>"><?php e((string) ($ticket['Company_Contact_Name'] ?? $ticketCustomerNo)); ?></a>
<?php else: ?>
<?php e((string) ($ticket['Company_Contact_Name'] ?? '—')); ?>
<?php endif; ?>
</dd>
</div>
<div class="helpdesk-ticket-meta-item">
<dt><?php e(t('Contact')); ?></dt>
<dd><?php e((string) ($ticket['Current_Contact_Name'] ?? '—')); ?></dd>
</div>
<div class="helpdesk-ticket-meta-item">
<dt><?php e(t('Category')); ?></dt>
<dd><?php e((string) ($ticket['Category_1_Description'] ?? '—')); ?></dd>
</div>
<div class="helpdesk-ticket-meta-item">
<dt><?php e(t('Support')); ?></dt>
<dd><?php e((string) ($ticket['Support_User_Name'] ?? '—')); ?></dd>
</div>
<div class="helpdesk-ticket-meta-item">
<dt><?php e(t('Debtor No.')); ?></dt>
<dd><?php e($ticketCustomerNo !== '' ? $ticketCustomerNo : '—'); ?></dd>
</div>
</dl>
</div>
</aside>
<?php endif; ?>
</div>
<!-- i18n strings for JS rendering -->
<script type="application/json" id="helpdesk-ticket-i18n"><?php gridJsonForJs([
'No communication found.' => t('No communication found.'),
'Could not load communication history.' => t('Could not load communication history.'),
'Live communication unavailable, showing activity fallback.' => t('Live communication unavailable, showing activity fallback.'),
'Ticket' => t('Ticket'),
'Unknown user' => t('Unknown user'),
'Unknown time' => t('Unknown time'),
'changed status' => t('changed status'),
'Forwarded' => t('Forwarded'),
'attached a file' => t('attached a file'),
'logged an activity' => t('logged an activity'),
'on' => t('on'),
]); ?></script>
<script type="module" src="<?php e(assetVersion('modules/helpdesk/js/helpdesk-ticket.js')); ?>"></script>