diff --git a/modules/addressbook/web/js/pages/address-book-index.js b/modules/addressbook/web/js/pages/address-book-index.js
index 0311b34..7860430 100644
--- a/modules/addressbook/web/js/pages/address-book-index.js
+++ b/modules/addressbook/web/js/pages/address-book-index.js
@@ -1,139 +1,136 @@
import { initMultiSelect } from '/js/components/app-multiselect-init.js';
import { initMultiSelectCascade } from '/js/components/app-multiselect-cascade.js';
-import { initStandardListPage } from '/js/core/app-grid-factory.js';
-import { readPageConfig } from '/js/core/app-page-config.js';
-import { warnOnce } from '/js/core/app-dom.js';
-import { getAppBase, escapeHtml, buildBadgeList, gridFilterMultiCsv, gridFilterText, withCurrentListReturn } from '/js/pages/app-list-utils.js';
+import { createListPageModule } from '/js/core/app-list-page-module.js';
+import { escapeHtml, buildBadgeList, gridFilterMultiCsv, gridFilterText, withCurrentListReturn } from '/js/pages/app-list-utils.js';
-const config = readPageConfig('address-book-index');
-if (config) {
- const appBase = getAppBase();
- const gridSearch = config.gridSearch ?? null;
- const filterSchema = config.filterSchema ?? [];
- const filterChipMeta = config.filterChipMeta ?? [];
- const labels = config.labels ?? {};
+createListPageModule({
+ configId: 'address-book-index',
+ moduleId: 'address-book-index',
+ missingGridMessage: 'Address book grid init failed: Grid.js missing',
+ initErrorMessage: 'Address book 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 toolbar = document.querySelector('#address-book-drawer-toolbar');
- const customFilterInputs = Array.from(document.querySelectorAll('[data-address-book-custom-filter]'));
- const tenantDepartmentMap = toolbar?.dataset?.tenantDeptMap
- ? JSON.parse(toolbar.dataset.tenantDeptMap)
- : {};
+ const toolbar = document.querySelector('#address-book-drawer-toolbar');
+ const customFilterInputs = Array.from(document.querySelectorAll('[data-address-book-custom-filter]'));
+ const tenantDepartmentMap = toolbar?.dataset?.tenantDeptMap
+ ? JSON.parse(toolbar.dataset.tenantDeptMap)
+ : {};
- const uuidIndex = 0;
+ const uuidIndex = 0;
- const initialsForName = (name) => {
- const parts = String(name || '')
- .trim()
- .split(/\s+/)
- .filter(Boolean);
- const chars = parts.slice(0, 2).map((value) => value[0] || '').join('').toUpperCase();
- return chars || '?';
- };
+ const initialsForName = (name) => {
+ const parts = String(name || '')
+ .trim()
+ .split(/\s+/)
+ .filter(Boolean);
+ const chars = parts.slice(0, 2).map((value) => value[0] || '').join('').toUpperCase();
+ return chars || '?';
+ };
- const customFilters = customFilterInputs.map((input) => {
- const param = String(input?.dataset?.addressBookCustomFilterParam || '').trim();
- if (!param) {return null;}
- if (input.dataset.addressBookCustomFilterType === 'multiselect') {
- const targetSelector = input.dataset.addressBookCustomFilterInput || '';
- return gridFilterMultiCsv(targetSelector, param, { default: [] });
- }
- return gridFilterText(input, param, { default: '' });
- }).filter(Boolean);
+ const customFilters = customFilterInputs.map((input) => {
+ const param = String(input?.dataset?.addressBookCustomFilterParam || '').trim();
+ if (!param) { return null; }
+ if (input.dataset.addressBookCustomFilterType === 'multiselect') {
+ const targetSelector = input.dataset.addressBookCustomFilterInput || '';
+ return gridFilterMultiCsv(targetSelector, param, { default: [] });
+ }
+ return gridFilterText(input, param, { default: '' });
+ }).filter(Boolean);
- const gridOptions = {
- gridjs: window.gridjs,
- container: '#address-book-grid',
- dataUrl: 'address-book/data',
- appBase,
- columns: [
- { name: 'UUID', hidden: true },
- {
- name: labels.name || 'Name',
- sort: true,
- formatter: (cell, row) => {
- const nameValue = typeof cell === 'object' && cell !== null
- ? (cell.name ?? '')
- : cell;
- const avatarUuid = typeof cell === 'object' && cell !== null
- ? (cell.avatar_uuid ?? '')
- : '';
- const name = escapeHtml(nameValue || '-');
- const uuid = row?.cells?.[uuidIndex]?.data;
- const editUrl = uuid ? withCurrentListReturn(new URL(`address-book/view/${uuid}`, appBase).toString()) : '';
- const nameHtml = editUrl
- ? `${name}`
- : `${name}`;
- if (!avatarUuid) {
- const initials = escapeHtml(initialsForName(nameValue));
- return gridjs.html(`${initials}${nameHtml}`);
- }
- const src = new URL(`admin/users/avatar-file?uuid=${avatarUuid}&size=64`, appBase).toString();
- const full = new URL(`admin/users/avatar-file?uuid=${avatarUuid}&size=256`, appBase).toString();
- return gridjs.html(`
${nameHtml}`);
+ const { gridConfig } = initListPage({
+ grid: {
+ gridjs,
+ container: '#address-book-grid',
+ dataUrl: 'address-book/data',
+ appBase,
+ columns: [
+ { name: 'UUID', hidden: true },
+ {
+ name: labels.name || 'Name',
+ sort: true,
+ formatter: (cell, row) => {
+ const nameValue = typeof cell === 'object' && cell !== null
+ ? (cell.name ?? '')
+ : cell;
+ const avatarUuid = typeof cell === 'object' && cell !== null
+ ? (cell.avatar_uuid ?? '')
+ : '';
+ const name = escapeHtml(nameValue || '-');
+ const uuid = row?.cells?.[uuidIndex]?.data;
+ const editUrl = uuid ? withCurrentListReturn(new URL(`address-book/view/${uuid}`, appBase).toString()) : '';
+ const nameHtml = editUrl
+ ? `${name}`
+ : `${name}`;
+ if (!avatarUuid) {
+ const initials = escapeHtml(initialsForName(nameValue));
+ return gridjs.html(`${initials}${nameHtml}`);
+ }
+ const src = new URL(`admin/users/avatar-file?uuid=${avatarUuid}&size=64`, appBase).toString();
+ const full = new URL(`admin/users/avatar-file?uuid=${avatarUuid}&size=256`, appBase).toString();
+ return gridjs.html(`
${nameHtml}`);
+ },
+ },
+ { name: labels.email || 'Email', sort: true },
+ { name: labels.phone || 'Phone', sort: false },
+ { name: labels.mobile || 'Mobile', sort: false },
+ { name: labels.shortDial || 'Short dial', sort: false },
+ { name: labels.tenants || 'Tenants', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) },
+ { name: labels.departments || 'Departments', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) },
+ { name: labels.roles || 'Roles', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) },
+ ],
+ sortColumns: [null, 'display_name', 'email', null, null, null, null, null, null],
+ paginationLimit: 10,
+ language: config.gridLang ?? {},
+ mapData: (data) => data.data.map((row) => [
+ row.uuid,
+ {
+ name: row.display_name,
+ avatar_uuid: row.has_avatar ? row.uuid : '',
+ },
+ row.email,
+ row.phone,
+ row.mobile,
+ row.short_dial,
+ row.tenants,
+ row.departments,
+ row.roles,
+ ]),
+ search: gridSearch,
+ filterSchema,
+ extraFilters: customFilters,
+ urlSync: true,
+ rowDataset: (row) => ({
+ uuid: row?.cells?.[uuidIndex]?.data,
+ }),
+ rowInteraction: { linkColumn: false },
+ rowDblClick: {
+ getUrl: (rowData) => {
+ const uuid = rowData?.cells?.[uuidIndex]?.data;
+ return uuid ? new URL(`address-book/view/${uuid}`, appBase).toString() : '';
+ },
},
},
- { name: labels.email || 'Email', sort: true },
- { name: labels.phone || 'Phone', sort: false },
- { name: labels.mobile || 'Mobile', sort: false },
- { name: labels.shortDial || 'Short dial', sort: false },
- { name: labels.tenants || 'Tenants', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) },
- { name: labels.departments || 'Departments', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) },
- { name: labels.roles || 'Roles', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) },
- ],
- sortColumns: [null, 'display_name', 'email', null, null, null, null, null, null],
- paginationLimit: 10,
- language: config.gridLang ?? {},
- mapData: (data) => data.data.map((row) => [
- row.uuid,
- {
- name: row.display_name,
- avatar_uuid: row.has_avatar ? row.uuid : '',
+ filters: {
+ mode: 'drawer',
+ chipMeta: filterChipMeta,
+ watchInputs: ['#address-book-search'],
},
- row.email,
- row.phone,
- row.mobile,
- row.short_dial,
- row.tenants,
- row.departments,
- row.roles,
- ]),
- search: gridSearch,
- filterSchema,
- extraFilters: customFilters,
- urlSync: true,
- rowDataset: (row) => ({
- uuid: row?.cells?.[uuidIndex]?.data,
- }),
- rowInteraction: { linkColumn: false },
- rowDblClick: {
- getUrl: (rowData) => {
- const uuid = rowData?.cells?.[uuidIndex]?.data;
- return uuid ? new URL(`address-book/view/${uuid}`, appBase).toString() : '';
- },
- },
- };
+ }) || {};
- const { gridConfig } = initStandardListPage({
- grid: gridOptions,
- filters: {
- mode: 'drawer',
- chipMeta: filterChipMeta,
- watchInputs: ['#address-book-search'],
- },
- });
-
- if (!gridConfig || !gridConfig.grid) {
- warnOnce('UI_INIT_FAIL', 'Address book grid init failed', { module: 'address-book-index', component: 'grid' });
- }
-
- initMultiSelect().then(() => {
- initMultiSelectCascade({
- parent: '#address-book-tenant-filter',
- child: '#address-book-department-filter',
- map: tenantDepartmentMap,
- mode: 'disable',
- clearInvalid: true,
+ initMultiSelect().then(() => {
+ initMultiSelectCascade({
+ parent: '#address-book-tenant-filter',
+ child: '#address-book-department-filter',
+ map: tenantDepartmentMap,
+ mode: 'disable',
+ clearInvalid: true,
+ });
});
- });
-}
+ return gridConfig;
+ },
+});
diff --git a/modules/audit/web/js/pages/admin-api-audit-index.js b/modules/audit/web/js/pages/admin-api-audit-index.js
index 1125711..a2d8645 100644
--- a/modules/audit/web/js/pages/admin-api-audit-index.js
+++ b/modules/audit/web/js/pages/admin-api-audit-index.js
@@ -1,147 +1,141 @@
-import { initStandardListPage } from '/js/core/app-grid-factory.js';
-import { readPageConfig } from '/js/core/app-page-config.js';
-import { warnOnce } from '/js/core/app-dom.js';
-import { getAppBase, withCurrentListReturn } from '/js/pages/app-list-utils.js';
+import { createListPageModule } from '/js/core/app-list-page-module.js';
+import { withCurrentListReturn } from '/js/pages/app-list-utils.js';
-const config = readPageConfig('admin-api-audit-index');
-if (config) {
- const appBase = getAppBase();
- const gridSearch = config.gridSearch ?? null;
- const filterSchema = config.filterSchema ?? [];
- const filterChipMeta = config.filterChipMeta ?? [];
- const labels = config.labels ?? {};
+createListPageModule({
+ configId: 'admin-api-audit-index',
+ moduleId: 'admin-api-audit-index',
+ missingGridMessage: 'API audit grid init failed: Grid.js missing',
+ initErrorMessage: 'API 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 initApiAuditGrid = () => {
- const gridOptions = {
- gridjs: window.gridjs,
- container: '#api-audit-grid',
- dataUrl: 'audit/api-audit/data',
- appBase,
- columns: [
- {
- name: labels.created || 'Created',
- sort: true,
- },
- {
- name: labels.statusCode || 'Status code',
- sort: true,
- formatter: (cell) => {
- if (!cell || typeof cell !== 'object') {
- return gridjs.html('');
- }
- return gridjs.html(`${cell.label || ''}`);
+ const { gridConfig } = initListPage({
+ grid: {
+ gridjs,
+ container: '#api-audit-grid',
+ dataUrl: 'audit/api-audit/data',
+ appBase,
+ columns: [
+ {
+ name: labels.created || 'Created',
+ sort: true,
},
- },
- {
- name: labels.method || 'Method',
- sort: true,
- formatter: (cell) => {
- const method = String(cell || '').toUpperCase();
- return gridjs.html(`${method}`);
+ {
+ name: labels.statusCode || 'Status code',
+ sort: true,
+ formatter: (cell) => {
+ if (!cell || typeof cell !== 'object') {
+ return gridjs.html('');
+ }
+ return gridjs.html(`${cell.label || ''}`);
+ },
},
- },
- {
- name: labels.path || 'Path',
- sort: true,
- },
- {
- name: labels.durationMs || 'Duration (ms)',
- sort: true,
- formatter: (cell) => (cell > 0 ? `${cell}` : '-'),
- },
- {
- name: labels.user || '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 = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
- return gridjs.html(`${label}`);
+ {
+ name: labels.method || 'Method',
+ sort: true,
+ formatter: (cell) => {
+ const method = String(cell || '').toUpperCase();
+ return gridjs.html(`${method}`);
+ },
},
- },
- {
- name: labels.tenant || 'Tenant',
- 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/tenants/edit/${cell.uuid}`, appBase).toString());
- return gridjs.html(`${label}`);
+ {
+ name: labels.path || 'Path',
+ sort: true,
+ },
+ {
+ name: labels.durationMs || 'Duration (ms)',
+ sort: true,
+ formatter: (cell) => (cell > 0 ? `${cell}` : '-'),
+ },
+ {
+ name: labels.user || '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 = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
+ return gridjs.html(`${label}`);
+ },
+ },
+ {
+ name: labels.tenant || 'Tenant',
+ 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/tenants/edit/${cell.uuid}`, appBase).toString());
+ return gridjs.html(`${label}`);
+ },
+ },
+ {
+ name: labels.errorCode || 'Error code',
+ sort: false,
+ formatter: (cell) => (cell ? gridjs.html(`${String(cell)}`) : gridjs.html('-')),
+ },
+ {
+ name: labels.ip || 'IP hash',
+ sort: false,
+ },
+ {
+ name: 'ID',
+ hidden: true,
+ },
+ ],
+ sortColumns: ['created_at', 'status_code', 'method', 'path', 'duration_ms', null, null, null, null, null],
+ paginationLimit: 10,
+ language: config.gridLang ?? {},
+ mapData: (data) => data.data.map((row) => [
+ row.created_at,
+ {
+ variant: row.status_badge,
+ label: String(row.status_code || ''),
+ },
+ row.method,
+ row.path,
+ row.duration_ms,
+ {
+ uuid: row.user_uuid || '',
+ label: row.user_label || '-',
+ },
+ {
+ uuid: row.tenant_uuid || '',
+ label: row.tenant_label || '-',
+ },
+ row.error_code || '',
+ row.ip_hash || '',
+ row.id,
+ ]),
+ rowInteraction: { linkColumn: 3 },
+ search: gridSearch,
+ filterSchema,
+ urlSync: true,
+ rowDblClick: {
+ getUrl: (rowData) => {
+ const id = rowData?.cells?.[9]?.data;
+ return id ? new URL(`audit/api-audit/view/${id}`, appBase).toString() : '';
},
- },
- {
- name: labels.errorCode || 'Error code',
- sort: false,
- formatter: (cell) => (cell ? gridjs.html(`${String(cell)}`) : gridjs.html('-')),
- },
- {
- name: labels.ip || 'IP hash',
- sort: false,
- },
- {
- name: 'ID',
- hidden: true,
- },
- ],
- sortColumns: ['created_at', 'status_code', 'method', 'path', 'duration_ms', null, null, null, null, null],
- paginationLimit: 10,
- language: config.gridLang ?? {},
- mapData: (data) => data.data.map((row) => [
- row.created_at,
- {
- variant: row.status_badge,
- label: String(row.status_code || ''),
- },
- row.method,
- row.path,
- row.duration_ms,
- {
- uuid: row.user_uuid || '',
- label: row.user_label || '-',
- },
- {
- uuid: row.tenant_uuid || '',
- label: row.tenant_label || '-',
- },
- row.error_code || '',
- row.ip_hash || '',
- row.id,
- ]),
- rowInteraction: { linkColumn: 3 },
- search: gridSearch,
- filterSchema,
- urlSync: true,
- rowDblClick: {
- getUrl: (rowData) => {
- const id = rowData?.cells?.[9]?.data;
- return id ? new URL(`audit/api-audit/view/${id}`, appBase).toString() : '';
},
},
- };
- const { gridConfig } = initStandardListPage({
- grid: gridOptions,
filters: {
mode: 'drawer',
chipMeta: filterChipMeta,
watchInputs: ['#api-audit-search'],
},
- });
- if (!gridConfig || !gridConfig.grid) {
- warnOnce('UI_INIT_FAIL', 'API audit grid init failed', { module: 'admin-api-audit-index', component: 'grid' });
- return null;
- }
- return gridConfig;
- };
+ }) || {};
- initApiAuditGrid();
-}
+ return gridConfig;
+ },
+});
diff --git a/modules/audit/web/js/pages/admin-import-audit-index.js b/modules/audit/web/js/pages/admin-import-audit-index.js
index 2587440..28347ea 100644
--- a/modules/audit/web/js/pages/admin-import-audit-index.js
+++ b/modules/audit/web/js/pages/admin-import-audit-index.js
@@ -1,104 +1,102 @@
-import { initStandardListPage } from '/js/core/app-grid-factory.js';
-import { readPageConfig } from '/js/core/app-page-config.js';
-import { warnOnce } from '/js/core/app-dom.js';
-import { getAppBase, withCurrentListReturn } from '/js/pages/app-list-utils.js';
+import { createListPageModule } from '/js/core/app-list-page-module.js';
+import { withCurrentListReturn } from '/js/pages/app-list-utils.js';
-const config = readPageConfig('admin-import-audit-index');
-if (config) {
- const appBase = getAppBase();
- const gridSearch = config.gridSearch ?? null;
- const filterSchema = config.filterSchema ?? [];
- const filterChipMeta = config.filterChipMeta ?? [];
- const labels = config.labels ?? {};
+createListPageModule({
+ configId: 'admin-import-audit-index',
+ moduleId: 'admin-import-audit-index',
+ missingGridMessage: 'Import audit grid init failed: Grid.js missing',
+ initErrorMessage: 'Import 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 profileLabel = (value) => {
- const key = String(value || '').toLowerCase();
- if (key === 'users') {return labels.users || 'Users';}
- if (key === 'departments') {return labels.departments || 'Departments';}
- return key || '-';
- };
+ const profileLabel = (value) => {
+ const key = String(value || '').toLowerCase();
+ if (key === 'users') { return labels.users || 'Users'; }
+ if (key === 'departments') { return labels.departments || 'Departments'; }
+ return key || '-';
+ };
- const gridOptions = {
- gridjs: window.gridjs,
- container: '#import-audit-grid',
- dataUrl: 'audit/import-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(`${cell.label || '-'}`);
+ const { gridConfig } = initListPage({
+ grid: {
+ gridjs,
+ container: '#import-audit-grid',
+ dataUrl: 'audit/import-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(`${cell.label || '-'}`);
+ },
+ },
+ {
+ name: labels.profile || 'Profile',
+ sort: true,
+ formatter: (cell) => profileLabel(cell),
+ },
+ { name: labels.durationMs || 'Duration (ms)', sort: true, formatter: (cell) => (cell > 0 ? `${cell}` : '-') },
+ { name: labels.rowsTotal || 'Rows total', sort: true },
+ { name: labels.createdCount || 'Created count', sort: true },
+ { name: labels.skippedCount || 'Skipped count', sort: true },
+ { name: labels.failedCount || 'Failed count', sort: true },
+ {
+ name: labels.user || '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 = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
+ return gridjs.html(`${label}`);
+ },
+ },
+ { 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: config.gridLang ?? {},
+ mapData: (data) => data.data.map((row) => [
+ row.started_at,
+ { variant: row.status_badge, label: row.status_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,
+ ]),
+ rowInteraction: { linkColumn: 2 },
+ search: gridSearch,
+ filterSchema,
+ urlSync: true,
+ rowDblClick: {
+ getUrl: (rowData) => {
+ const id = rowData?.cells?.[9]?.data;
+ return id ? new URL(`audit/import-audit/view/${id}`, appBase).toString() : '';
+ },
},
},
- {
- name: labels.profile || 'Profile',
- sort: true,
- formatter: (cell) => profileLabel(cell),
+ filters: {
+ mode: 'drawer',
+ chipMeta: filterChipMeta,
+ watchInputs: ['#import-audit-search'],
},
- { name: labels.durationMs || 'Duration (ms)', sort: true, formatter: (cell) => (cell > 0 ? `${cell}` : '-') },
- { name: labels.rowsTotal || 'Rows total', sort: true },
- { name: labels.createdCount || 'Created count', sort: true },
- { name: labels.skippedCount || 'Skipped count', sort: true },
- { name: labels.failedCount || 'Failed count', sort: true },
- {
- name: labels.user || '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 = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
- return gridjs.html(`${label}`);
- },
- },
- { 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: config.gridLang ?? {},
- mapData: (data) => data.data.map((row) => [
- row.started_at,
- { variant: row.status_badge, label: row.status_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,
- ]),
- rowInteraction: { linkColumn: 2 },
- search: gridSearch,
- filterSchema,
- urlSync: true,
- rowDblClick: {
- getUrl: (rowData) => {
- const id = rowData?.cells?.[9]?.data;
- return id ? new URL(`audit/import-audit/view/${id}`, appBase).toString() : '';
- },
- },
- };
+ }) || {};
- const { gridConfig } = initStandardListPage({
- grid: gridOptions,
- filters: {
- mode: 'drawer',
- chipMeta: filterChipMeta,
- watchInputs: ['#import-audit-search'],
- },
- });
-
- if (!gridConfig || !gridConfig.grid) {
- warnOnce('UI_INIT_FAIL', 'Import audit grid init failed', { module: 'admin-import-audit-index', component: 'grid' });
- }
-}
+ return gridConfig;
+ },
+});
diff --git a/modules/audit/web/js/pages/admin-system-audit-index.js b/modules/audit/web/js/pages/admin-system-audit-index.js
index 58e1faa..13cf8a6 100644
--- a/modules/audit/web/js/pages/admin-system-audit-index.js
+++ b/modules/audit/web/js/pages/admin-system-audit-index.js
@@ -1,107 +1,102 @@
-import { initStandardListPage } from '/js/core/app-grid-factory.js';
-import { readPageConfig } from '/js/core/app-page-config.js';
-import { warnOnce } from '/js/core/app-dom.js';
-import { getAppBase, withCurrentListReturn } from '/js/pages/app-list-utils.js';
+import { createListPageModule } from '/js/core/app-list-page-module.js';
+import { withCurrentListReturn } from '/js/pages/app-list-utils.js';
-const config = readPageConfig('admin-system-audit-index');
-if (config) {
- const appBase = getAppBase();
- const gridSearch = config.gridSearch ?? null;
- const filterSchema = config.filterSchema ?? [];
- const filterChipMeta = config.filterChipMeta ?? [];
- const labels = config.labels ?? {};
+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 initSystemAuditGrid = () => {
- const gridOptions = {
- gridjs: window.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(`${cell.label || ''}`);
+ 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(`${cell.label || ''}`);
+ },
},
- },
- { 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(`${label}`);
+ { 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(`${label}`);
+ },
+ },
+ { name: labels.targetType || 'Target type', sort: false },
+ {
+ name: labels.requestId || 'Request ID',
+ sort: false,
+ formatter: (cell) => (cell ? gridjs.html(`${String(cell).slice(0, 8)}`) : gridjs.html('-')),
+ },
+ {
+ name: labels.errorCode || 'Error code',
+ sort: false,
+ formatter: (cell) => (cell ? gridjs.html(`${String(cell)}`) : 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() : '';
},
- },
- { name: labels.targetType || 'Target type', sort: false },
- {
- name: labels.requestId || 'Request ID',
- sort: false,
- formatter: (cell) => (cell ? gridjs.html(`${String(cell).slice(0, 8)}`) : gridjs.html('-')),
- },
- {
- name: labels.errorCode || 'Error code',
- sort: false,
- formatter: (cell) => (cell ? gridjs.html(`${String(cell)}`) : 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() : '';
},
},
- };
- const { gridConfig } = initStandardListPage({
- grid: gridOptions,
filters: {
mode: 'drawer',
chipMeta: filterChipMeta,
watchInputs: ['#system-audit-search'],
},
- });
- return gridConfig;
- };
+ }) || {};
- const gridConfig = initSystemAuditGrid();
- if (!gridConfig || !gridConfig.grid) {
- warnOnce('UI_INIT_FAIL', 'System audit grid init failed', { module: 'admin-system-audit-index', component: 'grid' });
- }
-}
+ return gridConfig;
+ },
+});
diff --git a/modules/audit/web/js/pages/admin-user-lifecycle-audit-index.js b/modules/audit/web/js/pages/admin-user-lifecycle-audit-index.js
index aa5647f..f1681aa 100644
--- a/modules/audit/web/js/pages/admin-user-lifecycle-audit-index.js
+++ b/modules/audit/web/js/pages/admin-user-lifecycle-audit-index.js
@@ -1,77 +1,74 @@
-import { initStandardListPage } from '/js/core/app-grid-factory.js';
-import { readPageConfig } from '/js/core/app-page-config.js';
-import { warnOnce } from '/js/core/app-dom.js';
-import { getAppBase } from '/js/pages/app-list-utils.js';
+import { createListPageModule } from '/js/core/app-list-page-module.js';
-const config = readPageConfig('admin-user-lifecycle-audit-index');
-if (config) {
- const appBase = getAppBase();
- const gridSearch = config.gridSearch ?? null;
- const filterSchema = config.filterSchema ?? [];
- const filterChipMeta = config.filterChipMeta ?? [];
- const labels = config.labels ?? {};
+createListPageModule({
+ configId: 'admin-user-lifecycle-audit-index',
+ moduleId: 'admin-user-lifecycle-audit-index',
+ missingGridMessage: 'User lifecycle audit grid init failed: Grid.js missing',
+ initErrorMessage: 'User lifecycle 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 gridOptions = {
- gridjs: window.gridjs,
- container: '#user-lifecycle-audit-grid',
- dataUrl: 'audit/user-lifecycle-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(`${cell.label || '-'}`);
+ const { gridConfig } = initListPage({
+ grid: {
+ gridjs,
+ container: '#user-lifecycle-audit-grid',
+ dataUrl: 'audit/user-lifecycle-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(`${cell.label || '-'}`);
+ },
+ },
+ { name: labels.action || 'Action', sort: true },
+ { name: labels.trigger || 'Trigger', sort: true },
+ { name: labels.targetEmail || 'Target email', sort: false },
+ { name: labels.targetUuid || 'Target UUID', sort: false },
+ { name: labels.reasonCode || 'Reason code', sort: false },
+ { name: labels.restoredAt || 'Restored at', sort: true },
+ { name: 'ID', hidden: true },
+ ],
+ sortColumns: ['created_at', 'status', 'action', 'trigger_type', null, null, null, 'restored_at', null],
+ paginationLimit: 10,
+ language: config.gridLang ?? {},
+ mapData: (data) => data.data.map((row) => [
+ row.created_at,
+ { variant: row.status_badge, label: row.status_label || row.status || '' },
+ row.action_label || row.action || '-',
+ row.trigger_type_label || row.trigger_type || '-',
+ row.target_user_email || '-',
+ row.target_user_uuid || '-',
+ row.reason_code || '-',
+ row.restored_at || '-',
+ row.id,
+ ]),
+ rowInteraction: { linkColumn: 2 },
+ search: gridSearch,
+ filterSchema,
+ urlSync: true,
+ rowDblClick: {
+ getUrl: (rowData) => {
+ const id = rowData?.cells?.[8]?.data;
+ return id ? new URL(`audit/user-lifecycle-audit/view/${id}`, appBase).toString() : '';
+ },
},
},
- { name: labels.action || 'Action', sort: true },
- { name: labels.trigger || 'Trigger', sort: true },
- { name: labels.targetEmail || 'Target email', sort: false },
- { name: labels.targetUuid || 'Target UUID', sort: false },
- { name: labels.reasonCode || 'Reason code', sort: false },
- { name: labels.restoredAt || 'Restored at', sort: true },
- { name: 'ID', hidden: true },
- ],
- sortColumns: ['created_at', 'status', 'action', 'trigger_type', null, null, null, 'restored_at', null],
- paginationLimit: 10,
- language: config.gridLang ?? {},
- mapData: (data) => data.data.map((row) => [
- row.created_at,
- { variant: row.status_badge, label: row.status_label || row.status || '' },
- row.action_label || row.action || '-',
- row.trigger_type_label || row.trigger_type || '-',
- row.target_user_email || '-',
- row.target_user_uuid || '-',
- row.reason_code || '-',
- row.restored_at || '-',
- row.id,
- ]),
- rowInteraction: { linkColumn: 2 },
- search: gridSearch,
- filterSchema,
- urlSync: true,
- rowDblClick: {
- getUrl: (rowData) => {
- const id = rowData?.cells?.[8]?.data;
- return id ? new URL(`audit/user-lifecycle-audit/view/${id}`, appBase).toString() : '';
+ filters: {
+ mode: 'drawer',
+ chipMeta: filterChipMeta,
+ watchInputs: ['#user-lifecycle-audit-search'],
},
- },
- };
+ }) || {};
- const { gridConfig } = initStandardListPage({
- grid: gridOptions,
- filters: {
- mode: 'drawer',
- chipMeta: filterChipMeta,
- watchInputs: ['#user-lifecycle-audit-search'],
- },
- });
-
- if (!gridConfig || !gridConfig.grid) {
- warnOnce('UI_INIT_FAIL', 'User lifecycle audit grid init failed', { module: 'admin-user-lifecycle-audit-index', component: 'grid' });
- }
-}
+ return gridConfig;
+ },
+});