Files
breadcrumb-the-shire/pages/admin/import-audit/index(default).phtml
fs 25370a1a55 add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00

196 lines
6.7 KiB
PHTML

<?php
use MintyPHP\Session;
$canPurgeImportAudit = can('settings.update');
?>
<?php
$breadcrumbs = [
['label' => t('Home'), 'path' => 'admin'],
['label' => t('Import audit logs')],
];
require templatePath('partials/app-breadcrumb.phtml');
?>
<div class="app-list-titlebar">
<h1><?php e(t('Import audit logs')); ?></h1>
<div class="app-list-titlebar-actions">
<?php if ($canPurgeImportAudit): ?>
<form id="import-audit-purge-form" method="post" action="admin/import-audit/purge">
<?php Session::getCsrfInput(); ?>
</form>
<button
type="submit"
form="import-audit-purge-form"
class="outline secondary"
onclick="return confirm('<?php e(t('Purge entries older than 90 days?')); ?>');"
>
<i class="bi bi-trash"></i> <?php e(t('Purge import logs')); ?>
</button>
<?php endif; ?>
<button
class="outline secondary"
type="button"
data-toolbar-toggle
data-toolbar-target="#import-audit-toolbar"
data-toolbar-text-show="<?php e(t('Show filters')); ?>"
data-toolbar-text-hide="<?php e(t('Hide filters')); ?>"
>
<i class="bi bi-funnel-fill"></i> <span data-toolbar-label><?php e(t('Hide filters')); ?></span>
</button>
</div>
</div>
<div class="app-list-toolbar" id="import-audit-toolbar">
<label class="app-field">
<span><?php e(t('Search')); ?></span>
<input type="search" id="import-audit-search" placeholder="<?php e(t('Search...')); ?>">
</label>
<label class="app-field">
<span><?php e(t('Profile')); ?></span>
<select id="import-audit-profile-filter">
<option value=""><?php e(t('All profiles')); ?></option>
<option value="users"><?php e(t('Users')); ?></option>
<option value="departments"><?php e(t('Departments')); ?></option>
</select>
</label>
<label class="app-field">
<span><?php e(t('Status')); ?></span>
<select id="import-audit-status-filter">
<option value=""><?php e(t('All statuses')); ?></option>
<option value="running"><?php e(t('Running')); ?></option>
<option value="success"><?php e(t('Success')); ?></option>
<option value="partial"><?php e(t('Partial')); ?></option>
<option value="failed"><?php e(t('Failed')); ?></option>
</select>
</label>
<label class="app-field">
<span><?php e(t('Created from')); ?></span>
<input type="date" id="import-audit-created-from">
</label>
<label class="app-field">
<span><?php e(t('Created to')); ?></span>
<input type="date" id="import-audit-created-to">
</label>
<label class="app-field">
<span><?php e(t('User ID')); ?></span>
<input type="number" id="import-audit-user-id" min="1" step="1">
</label>
</div>
<div class="app-list-table">
<div id="import-audit-grid"></div>
</div>
<script src="<?php e(asset('vendor/gridjs/gridjs.umd.js')); ?>"></script>
<script type="module">
import { createServerGrid } from "<?php e(assetVersion('js/core/app-grid-factory.js')); ?>";
import { getAppBase } from "<?php e(asset('js/pages/app-list-utils.js')); ?>";
const appBase = getAppBase();
const profileLabel = (value) => {
const key = String(value || '').toLowerCase();
if (key === 'users') {return "<?php e(t('Users')); ?>";}
if (key === 'departments') {return "<?php e(t('Departments')); ?>";}
return key || '-';
};
const statusLabel = (value) => {
const key = String(value || '').toLowerCase();
if (key === 'running') {return "<?php e(t('Running')); ?>";}
if (key === 'success') {return "<?php e(t('Success')); ?>";}
if (key === 'partial') {return "<?php e(t('Partial')); ?>";}
if (key === 'failed') {return "<?php e(t('Failed')); ?>";}
return key || '-';
};
const gridConfig = createServerGrid({
gridjs: window.gridjs,
container: '#import-audit-grid',
dataUrl: 'admin/import-audit/data',
appBase,
columns: [
{name: "<?php e(t('Created')); ?>", sort: true},
{
name: "<?php e(t('Status')); ?>",
sort: true,
formatter: (cell) => {
if (!cell || typeof cell !== 'object') {
return gridjs.html('-');
}
return gridjs.html(`<span class="badge" data-variant="${cell.variant || 'neutral'}">${statusLabel(cell.label || '')}</span>`);
}
},
{
name: "<?php e(t('Profile')); ?>",
sort: true,
formatter: (cell) => profileLabel(cell)
},
{name: "<?php e(t('Duration (ms)')); ?>", sort: true, formatter: (cell) => (cell > 0 ? `${cell}` : '-')},
{name: "<?php e(t('Rows total')); ?>", sort: true},
{name: "<?php e(t('Created count')); ?>", sort: true},
{name: "<?php e(t('Skipped count')); ?>", sort: true},
{name: "<?php e(t('Failed count')); ?>", sort: true},
{
name: "<?php e(t('User')); ?>",
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 = new URL(`admin/users/edit/${cell.uuid}`, appBase).toString();
return gridjs.html(`<a href="${url}">${label}</a>`);
}
},
{name: 'ID', hidden: true}
],
sortColumns: ['started_at', 'status', 'profile_key', 'duration_ms', 'rows_total', 'created_count', 'skipped_count', 'failed_count', null, null],
paginationLimit: 10,
language: <?php MintyPHP\Buffer::get('grid_lang'); ?>,
mapData: (data) => data.data.map((row) => [
row.started_at,
{variant: row.status_badge, label: row.status || ''},
row.profile_key,
row.duration_ms,
row.rows_total,
row.created_count,
row.skipped_count,
row.failed_count,
{uuid: row.user_uuid || '', label: row.user_label || '-'},
row.id
]),
actions: {enabled: false},
search: {
input: '#import-audit-search',
param: 'search',
debounce: 250
},
filters: [
{input: '#import-audit-profile-filter', param: 'profile_key'},
{input: '#import-audit-status-filter', param: 'status'},
{input: '#import-audit-created-from', param: 'created_from'},
{input: '#import-audit-created-to', param: 'created_to'},
{
input: '#import-audit-user-id',
param: 'user_id',
normalize: (value) => {
const n = Number(value);
return Number.isInteger(n) && n > 0 ? String(n) : '';
}
}
],
urlSync: true,
rowDblClick: {
getUrl: (rowData) => {
const id = rowData?.cells?.[9]?.data;
return id ? new URL(`admin/import-audit/view/${id}`, appBase).toString() : '';
}
}
});
if (!gridConfig || !gridConfig.grid) {
console.warn('Import audit grid init failed');
}
</script>