import { initStandardListPage } from '../core/app-grid-factory.js'; import { readPageConfig } from '../core/app-page-config.js'; import { warnOnce } from '../core/app-dom.js'; import { initUsersListPage } from './app-users-list.js'; import { bindBulkVisibility } from '../components/app-bulk-selection.js'; import { buildUrl, badgeHtml, getAppBase, escapeHtml, buildBadgeList } from './app-list-utils.js'; import { showAsyncFlash, withLoading } from '../components/app-async-flash.js'; const config = readPageConfig('admin-users-index'); if (config) { const appBase = getAppBase(); const gridSearch = config.gridSearch ?? null; const filterSchema = config.filterSchema ?? []; const usersFilterChipMeta = config.filterChipMeta ?? []; const currentUserUuid = String(config.currentUserUuid || ''); const canUpdateUsers = config.canUpdateUsers === true; const canUpdateSelf = config.canUpdateSelf === true; const csrf = config.csrf ?? null; const labels = config.labels ?? {}; const buildBulkUrl = (action) => buildUrl(appBase, `admin/users/bulk/${action}`); const initUsersGrid = () => { const rowSelection = window.gridjs?.plugins?.selection?.RowSelection; if (!rowSelection) { throw new Error('Grid.js RowSelection plugin is not available.'); } const activeIndex = 6; const uuidIndex = 2; const canEditRow = (row) => { const uuid = row?.cells?.[uuidIndex]?.data; if (!uuid) {return false;} if (uuid === currentUserUuid) { return canUpdateUsers || canUpdateSelf; } return canUpdateUsers; }; const initialsForRow = (row) => { const first = row?.cells?.[3]?.data ?? ''; const last = row?.cells?.[4]?.data ?? ''; const parts = [first, last] .map((value) => String(value || '').trim()) .filter(Boolean); const chars = parts.map((value) => value[0] || '').join('').toUpperCase(); return chars || '?'; }; const columns = [ { name: labels.avatar || 'Avatar', sort: false, formatter: (cell, row) => { if (!cell) { const initials = escapeHtml(initialsForRow(row)); return gridjs.html(`${initials}`); } const src = new URL(`admin/users/avatar-file?uuid=${cell}&size=64`, appBase).toString(); const full = new URL(`admin/users/avatar-file?uuid=${cell}&size=256`, appBase).toString(); return gridjs.html(``); }, }, { name: 'UUID', hidden: true }, { name: labels.firstName || 'First name', sort: true }, { name: labels.lastName || 'Last name', sort: true }, { name: labels.email || 'Email', sort: true }, { name: labels.state || 'State', sort: true, formatter: (cell) => cell ? gridjs.html(`${labels.active || 'Active'}`) : gridjs.html(`${labels.inactive || 'Inactive'}`), }, { name: labels.tenants || 'Tenants', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell, { primaryTooltip: labels.primaryTenant || '' })), }, { name: labels.departments || 'Departments', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) }, { name: labels.roles || 'Roles', sort: false, formatter: (cell) => gridjs.html(buildBadgeList(cell)) }, { name: labels.phone || 'Phone', sort: false }, { name: labels.mobile || 'Mobile', sort: false }, { name: labels.shortDial || 'Short dial', sort: false }, { name: labels.created || 'Created', sort: true, formatter: (cell) => badgeHtml(gridjs, cell) }, { name: labels.modified || 'Modified', sort: true, formatter: (cell) => badgeHtml(gridjs, cell) }, { name: labels.lastLogin || 'Last login', sort: true, formatter: (cell) => badgeHtml(gridjs, cell || labels.never || 'Never'), }, { name: 'ID', hidden: true }, ]; const gridOptions = { gridjs: window.gridjs, container: '#users-grid', dataUrl: 'admin/users/data', appBase, columns, sortColumns: [null, null, 'first_name', 'last_name', 'email', 'active', null, null, null, null, null, null, 'created', 'modified', 'last_login_at', null], paginationLimit: 10, language: config.gridLang ?? {}, mapData: (data) => data.data.map((row) => [ row.has_avatar ? row.uuid : '', row.uuid, row.first_name, row.last_name, row.email, row.active, row.tenants, row.departments, row.roles, row.phone, row.mobile, row.short_dial, row.created, row.modified, row.last_login, row.id, ]), selection: { enabled: true, id: 'selectRow', component: rowSelection, selectAllLabel: labels.selectAll || 'Select all', props: { id: (row) => row.cell(uuidIndex).data, }, getSelectedIds: ({ container }) => { if (!container) {return [];} return Array.from(container.querySelectorAll('.gridjs-tr-selected')) .map((row) => row.getAttribute('data-uuid')) .filter(Boolean); }, }, search: gridSearch, filterSchema, urlSync: true, rowDataset: (row) => ({ uuid: row?.cells?.[uuidIndex]?.data, }), rowInteraction: { linkColumn: 2 }, rowDblClick: { getUrl: (rowData) => { if (!canEditRow(rowData)) { return ''; } const uuid = rowData?.cells?.[uuidIndex]?.data; return uuid ? new URL(`admin/users/edit/${uuid}`, appBase).toString() : ''; }, }, }; const { gridConfig } = initStandardListPage({ grid: gridOptions, filters: { mode: 'drawer', chipMeta: usersFilterChipMeta, watchInputs: ['#user-search'], preserveFilterParamsOnReset: ['tenant'], drawer: { countExcludeParams: ['tenant'], }, clearMetaOptions: { preserveFilterParams: ['tenant'], }, }, }); if (!gridConfig || !gridConfig.grid) { warnOnce('UI_INIT_FAIL', 'Users grid init failed', { module: 'admin-users-index', component: 'grid' }); return null; } return { ...gridConfig, indices: { activeIndex, uuidIndex } }; }; const gridConfig = initUsersGrid(); initUsersListPage({ gridConfig, appBase, csrf, labels, buildBulkUrl, bulkDownloadForms: { 'access-pdf': '#users-access-pdf-bulk-form', }, resetOptions: { resetPage: true, urlHistoryMode: 'push', preserveFilterParams: ['tenant'], }, showFlash: showAsyncFlash, withLoading, }); bindBulkVisibility({ containerSelector: '#users-grid', buttonSelector: '[data-users-bulk]', }); }