feat(helpdesk): ticket detail drawer — third drawer consumer
Migrates the ticket detail view (communication feed + ticket metadata aside)
into the core detail drawer pattern. Click a ticket number in the customer's
support tab → drawer slides in with the full ticket thread; arrow icons step
through the current filter page; ↗ opens the full page.
- `modules/helpdesk/templates/helpdesk-ticket-detail.phtml` extracts the
body (communication feed container + metadata aside + i18n JSON) so the
full page and the fragment render from the same source.
- `ticket-fragment($id).php` / `(none).phtml` reuse BcODataGateway::getTicket
and enforce the same ABILITY_ACCESS guard as the full page. Connection
errors render inline without aborting the fragment.
- Route `helpdesk/ticket-fragment/{id}` declared in module.php.
- `helpdesk-detail.js` wires the tickets grid: ticket-no formatter emits
`data-drawer-trigger`, `linkColumn` disabled, `initDetailDrawer` called
with `onContentLoaded → initHelpdeskTicket(contentEl)` so the async
communication feed loads inside the drawer just like on the full page.
- `fetchUrl`/`fullUrl` written as single-expression arrows so the
DetailDrawerFragmentContractTest can discover the consumer and validate
the fragment endpoint files exist — the test now covers all three drawer
consumers (address book, admin users, helpdesk tickets).
The full-page ticket view is unchanged for users who deep-link directly;
only the router-to-body wiring moved through the partial.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import { initListSection } from '/js/core/app-list-page-module.js';
|
||||
import { getJson } from '/js/core/app-http.js';
|
||||
import { resolveHost } from '/js/core/app-dom.js';
|
||||
import { readPageConfig } from '/js/core/app-page-config.js';
|
||||
import { initDetailDrawer } from '/js/components/app-detail-drawer.js';
|
||||
import { initHelpdeskTicket } from './helpdesk-ticket.js';
|
||||
import {
|
||||
renderCommunicationError,
|
||||
renderCommunicationFeed,
|
||||
@@ -776,7 +778,7 @@ function init(container) {
|
||||
{
|
||||
name: labels.ticketNo || 'Ticket No.',
|
||||
sort: true,
|
||||
formatter: (cell) => gridjs.html(`<a href="${cell.url}">${cell.no}</a>`),
|
||||
formatter: (cell) => gridjs.html(`<a class="app-grid-link-cell" href="${cell.url}" data-drawer-trigger data-ticket-no="${cell.no || ''}">${cell.no}</a>`),
|
||||
},
|
||||
{ name: labels.description || 'Description', sort: false },
|
||||
{
|
||||
@@ -816,10 +818,13 @@ function init(container) {
|
||||
return url || '';
|
||||
},
|
||||
},
|
||||
rowInteraction: { linkColumn: 0 },
|
||||
rowInteraction: { linkColumn: false },
|
||||
rowDataset: (row) => ({
|
||||
uuid: row?.cells?.[0]?.data?.no || '',
|
||||
}),
|
||||
};
|
||||
|
||||
initListSection({
|
||||
const section = initListSection({
|
||||
moduleId: 'helpdesk-detail-tickets',
|
||||
initErrorMessage: 'Helpdesk tickets grid init failed',
|
||||
initOptions: {
|
||||
@@ -832,6 +837,26 @@ function init(container) {
|
||||
},
|
||||
});
|
||||
ticketsGridInitialized = true;
|
||||
|
||||
initDetailDrawer({
|
||||
gridConfig: section?.gridConfig || null,
|
||||
triggerSelector: '[data-drawer-trigger][data-ticket-no]',
|
||||
rowUuidAttr: 'uuid',
|
||||
fetchUrl: (ticketNo) => new URL(`helpdesk/ticket-fragment/${encodeURIComponent(ticketNo)}?from=${encodeURIComponent(customerNo || '')}`, appBase).toString(),
|
||||
fullUrl: (ticketNo) => new URL(`helpdesk/ticket/${encodeURIComponent(ticketNo)}?from=${encodeURIComponent(customerNo || '')}`, appBase).toString(),
|
||||
hashPrefix: 'ticket',
|
||||
onContentLoaded: (contentEl) => {
|
||||
try { initHelpdeskTicket(contentEl); } catch (err) { console.warn('[helpdesk-detail] initHelpdeskTicket failed', err); }
|
||||
},
|
||||
labels: {
|
||||
close: labels.drawerClose || 'Close',
|
||||
previous: labels.drawerPrev || 'Previous',
|
||||
next: labels.drawerNext || 'Next',
|
||||
openFull: labels.drawerOpenFull || 'Open full page',
|
||||
loading: labels.drawerLoading || 'Loading',
|
||||
error: labels.drawerError || 'Failed to load',
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
ticketsGridInitializing = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user