feat(core): UserProfileViewService + admin/users detail drawer
Extracts the user profile read-view into a core service + shared partial so
modules no longer duplicate the assignment/scope logic, and migrates the
admin/users list as the drawer's second consumer.
Consolidation:
- `core/Service/User/UserProfileViewService` owns the single read-path that
resolves tenants/departments/roles for a user UUID, enforces scope-aware
department visibility, and returns a consistent `{status, user}` shape.
- `templates/partials/app-user-profile.phtml` is the shared render template;
takes `$profileKey` for storage namespace + lightbox grouping.
- `AddressBookService::buildViewContext()` shrinks to a one-line delegation
and drops its `UserAssignmentService` dependency. The old
`modules/addressbook/templates/address-book-profile.phtml` is removed.
admin/users migration:
- `pages/admin/users/view-fragment($id).php` + `(none).phtml` use the core
service and enforce `ABILITY_ADMIN_USERS_VIEW`.
- `admin-users-index.js` replaces the grid-native `linkColumn` on the first
name with a formatter that emits `data-drawer-trigger`; `linkColumn` is
disabled so the drawer click handler wins. `fullUrl` points at the edit
form — drawer → edit is one click.
- Drawer labels wired via page config.
The new `DetailDrawerFragmentContractTest` validated the users fragment
endpoint automatically — no manual test additions were needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { createListPageModule } from '../core/app-list-page-module.js';
|
||||
import { initUsersListPage } from './app-users-list.js';
|
||||
import { bindBulkVisibility } from '../components/app-bulk-selection.js';
|
||||
import { buildUrl, badgeHtml, escapeHtml, buildBadgeList } from './app-list-utils.js';
|
||||
import { initDetailDrawer } from '../components/app-detail-drawer.js';
|
||||
import { buildUrl, badgeHtml, escapeHtml, buildBadgeList, withCurrentListReturn } from './app-list-utils.js';
|
||||
import { showAsyncFlash, withLoading } from '../components/app-async-flash.js';
|
||||
|
||||
createListPageModule({
|
||||
@@ -61,7 +62,17 @@ createListPageModule({
|
||||
},
|
||||
},
|
||||
{ name: 'UUID', hidden: true },
|
||||
{ name: labels.firstName || 'First name', sort: true },
|
||||
{
|
||||
name: labels.firstName || 'First name',
|
||||
sort: true,
|
||||
formatter: (cell, row) => {
|
||||
const name = escapeHtml(String(cell || '').trim() || '-');
|
||||
const uuid = row?.cells?.[uuidIndex]?.data;
|
||||
if (!uuid) { return gridjs.html(`<span>${name}</span>`); }
|
||||
const editUrl = escapeHtml(withCurrentListReturn(new URL(`admin/users/edit/${uuid}`, appBase).toString()));
|
||||
return gridjs.html(`<a class="app-grid-link-cell" href="${editUrl}" data-drawer-trigger>${name}</a>`);
|
||||
},
|
||||
},
|
||||
{ name: labels.lastName || 'Last name', sort: true },
|
||||
{ name: labels.email || 'Email', sort: true },
|
||||
{
|
||||
@@ -140,7 +151,7 @@ createListPageModule({
|
||||
rowDataset: (row) => ({
|
||||
uuid: row?.cells?.[uuidIndex]?.data,
|
||||
}),
|
||||
rowInteraction: { linkColumn: 2 },
|
||||
rowInteraction: { linkColumn: false },
|
||||
rowDblClick: {
|
||||
getUrl: (rowData) => {
|
||||
if (!canEditRow(rowData)) {
|
||||
@@ -190,6 +201,23 @@ createListPageModule({
|
||||
buttonSelector: '[data-users-bulk]',
|
||||
});
|
||||
|
||||
initDetailDrawer({
|
||||
gridConfig,
|
||||
triggerSelector: '[data-drawer-trigger]',
|
||||
rowUuidAttr: 'uuid',
|
||||
fetchUrl: (uuid) => new URL(`admin/users/view-fragment/${uuid}`, appBase).toString(),
|
||||
fullUrl: (uuid) => withCurrentListReturn(new URL(`admin/users/edit/${uuid}`, appBase).toString()),
|
||||
hashPrefix: 'user',
|
||||
labels: {
|
||||
close: labels.drawerClose || 'Close',
|
||||
previous: labels.drawerPrev || 'Previous',
|
||||
next: labels.drawerNext || 'Next',
|
||||
openFull: labels.drawerOpenFull || 'Open full page',
|
||||
loading: labels.drawerLoading || 'Loading',
|
||||
error: labels.drawerError || 'Failed to load',
|
||||
},
|
||||
});
|
||||
|
||||
return usersGridConfig;
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user