Third consumer of the generic export primitive. Adds a
GET /admin/system-audit/export endpoint, the reusable export dropdown
to the list titlebar, and wires initListExport() into the page JS.
- pages/audit/system-audit/export().php: guard + ABILITY_SYSTEM_AUDIT_VIEW,
reuses the existing filter-schema.php via gridParseFiltersFromSchemaFile
so exported rows match the grid under the same filters, caps the limit
at 5000, and declares 11 ExportColumns (ID, created, event, status,
channel, actor, actor email, target type/uuid, request id, error code)
with translated enum labels for outcome/channel.
- module.php: new admin/system-audit/export route.
- index(default).phtml: app-list-export-dropdown partial placed above the
existing purge action; exportUrl added to pageConfig via
lurl('admin/system-audit/export').
- admin-system-audit-index.js: imports initListExport and invokes it
whenever config.exportUrl is present.
- i18n de/en: new "Actor email" key (required by TranslationKeysTest).
Gates: PHPUnit 1880 OK, PHPStan 0 errors, module:sync ok.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
3.8 KiB
JavaScript
108 lines
3.8 KiB
JavaScript
import { createListPageModule } from '/js/core/app-list-page-module.js';
|
|
import { withCurrentListReturn } from '/js/pages/app-list-utils.js';
|
|
import { initListExport } from '/js/core/app-list-export.js';
|
|
|
|
createListPageModule({
|
|
configId: 'admin-system-audit-index',
|
|
moduleId: 'admin-system-audit-index',
|
|
missingGridMessage: 'System audit grid init failed: Grid.js missing',
|
|
initErrorMessage: 'System audit grid init failed',
|
|
setup: ({ config, gridjs, appBase, initListPage }) => {
|
|
const labels = config.labels ?? {};
|
|
const gridSearch = config.gridSearch ?? null;
|
|
const filterSchema = config.filterSchema ?? [];
|
|
const filterChipMeta = config.filterChipMeta ?? [];
|
|
|
|
const { gridConfig } = initListPage({
|
|
grid: {
|
|
gridjs,
|
|
container: '#system-audit-grid',
|
|
dataUrl: 'audit/system-audit/data',
|
|
appBase,
|
|
columns: [
|
|
{ name: labels.created || 'Created', sort: true },
|
|
{
|
|
name: labels.status || 'Status',
|
|
sort: true,
|
|
formatter: (cell) => {
|
|
if (!cell || typeof cell !== 'object') {
|
|
return gridjs.html('');
|
|
}
|
|
return gridjs.html(`<span class="badge" data-variant="${cell.variant || 'neutral'}">${cell.label || ''}</span>`);
|
|
},
|
|
},
|
|
{ name: labels.event || 'Event', sort: true },
|
|
{ name: labels.channel || 'Channel', sort: true },
|
|
{
|
|
name: labels.actor || 'Actor',
|
|
sort: false,
|
|
formatter: (cell) => {
|
|
if (!cell || typeof cell !== 'object') {
|
|
return gridjs.html('-');
|
|
}
|
|
const label = cell.label || '-';
|
|
if (!cell.uuid) {
|
|
return gridjs.html(label);
|
|
}
|
|
const url = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
|
|
return gridjs.html(`<a href="${url}">${label}</a>`);
|
|
},
|
|
},
|
|
{ name: labels.targetType || 'Target type', sort: false },
|
|
{
|
|
name: labels.requestId || 'Request ID',
|
|
sort: false,
|
|
formatter: (cell) => (cell ? gridjs.html(`<code>${String(cell).slice(0, 8)}</code>`) : gridjs.html('-')),
|
|
},
|
|
{
|
|
name: labels.errorCode || 'Error code',
|
|
sort: false,
|
|
formatter: (cell) => (cell ? gridjs.html(`<code>${String(cell)}</code>`) : gridjs.html('-')),
|
|
},
|
|
{ name: 'ID', hidden: true },
|
|
],
|
|
sortColumns: ['created_at', 'outcome', 'event_type', 'channel', null, null, null, null, null],
|
|
paginationLimit: 10,
|
|
language: config.gridLang ?? {},
|
|
mapData: (data) => data.data.map((row) => [
|
|
row.created_at,
|
|
{
|
|
variant: row.outcome_badge,
|
|
label: String(row.outcome_label || row.outcome || ''),
|
|
},
|
|
row.event_type,
|
|
row.channel,
|
|
{
|
|
uuid: row.actor_user_uuid || '',
|
|
label: row.actor_user_label || '-',
|
|
},
|
|
row.target_type || '-',
|
|
row.request_id || '',
|
|
row.error_code || '',
|
|
row.id,
|
|
]),
|
|
rowInteraction: { linkColumn: 2 },
|
|
search: gridSearch,
|
|
filterSchema,
|
|
rowDblClick: {
|
|
getUrl: (rowData) => {
|
|
const id = rowData?.cells?.[8]?.data;
|
|
return id ? new URL(`audit/system-audit/view/${id}`, appBase).toString() : '';
|
|
},
|
|
},
|
|
},
|
|
filters: {
|
|
mode: 'drawer',
|
|
chipMeta: filterChipMeta,
|
|
watchInputs: ['#system-audit-search'],
|
|
},
|
|
}) || {};
|
|
|
|
if (gridConfig && config.exportUrl) {
|
|
initListExport({ gridConfig, exportUrl: config.exportUrl });
|
|
}
|
|
|
|
return gridConfig;
|
|
},
|
|
});
|