From 5219bbaff2ad50e502abec6ee45e5ee3a78697bd Mon Sep 17 00:00:00 2001 From: fs Date: Mon, 20 Apr 2026 22:45:54 +0200 Subject: [PATCH] refactor(js): migrate audit and addressbook list pages to list factory --- .../web/js/pages/address-book-index.js | 247 ++++++++--------- .../web/js/pages/admin-api-audit-index.js | 260 +++++++++--------- .../web/js/pages/admin-import-audit-index.js | 192 +++++++------ .../web/js/pages/admin-system-audit-index.js | 185 ++++++------- .../pages/admin-user-lifecycle-audit-index.js | 139 +++++----- 5 files changed, 502 insertions(+), 521 deletions(-) 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; + }, +});