1
0

feat(sidebar): render module-contributed admin nav items via sidebar.admin_nav_item slot

The sidebar template now reads sidebar.admin_nav_item UI slots from
modules and merges them into the matching admin nav group (by group
key). Module permissions are resolved via layoutAuth which already
includes module UI abilities from ModuleRegistry::getModuleUiAbilities().

This restores audit nav items (API audit, System audit, Import logs,
User lifecycle logs) in the Logs group when the audit module is active.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 08:16:58 +01:00
parent e10b1215da
commit 83d91789c1

View File

@@ -169,6 +169,39 @@ $adminNavGroups = [
],
];
// ── Merge module-contributed sidebar nav items into admin groups ──
if ($moduleNavItemSlots) {
// Index groups by key for fast lookup
$groupIndex = [];
foreach ($adminNavGroups as $idx => $group) {
$groupIndex[$group['key'] ?? ''] = $idx;
}
// Sort module items by order, then append to matching group
usort($moduleNavItemSlots, static fn ($a, $b) => ($a['order'] ?? 100) <=> ($b['order'] ?? 100));
foreach ($moduleNavItemSlots as $slot) {
if (!is_array($slot)) { continue; }
$targetGroup = trim((string) ($slot['group'] ?? ''));
$slotPath = trim((string) ($slot['path'] ?? ''));
$slotLabel = trim((string) ($slot['label'] ?? ''));
$slotPermission = trim((string) ($slot['permission'] ?? ''));
if ($targetGroup === '' || $slotPath === '' || $slotLabel === '') { continue; }
// Check permission via layoutAuth (module permissions are merged into layout capabilities)
$slotVisible = $slotPermission === '' || !empty($layoutAuth[$slotPermission]);
if (!isset($groupIndex[$targetGroup])) { continue; }
$gIdx = $groupIndex[$targetGroup];
$adminNavGroups[$gIdx]['items'][] = [
'label' => t($slotLabel),
'path' => $slotPath,
'active' => navActive($slotPath, true),
'visible' => $slotVisible,
'withTenant' => false,
];
if ($slotVisible) {
$adminNavGroups[$gIdx]['visible'] = true;
}
}
}
// Render helper for grouped admin aside navigation
$renderAdminNavGroup = static function (array $group, string $tenantQueryParam): void {
if (empty($group['visible'])) {
@@ -233,6 +266,7 @@ $csrfToken = (string) ($layoutNav['csrfToken'] ?? '');
$moduleSlots = is_array($layoutNav['moduleSlots'] ?? null) ? $layoutNav['moduleSlots'] : [];
$modulePanelSlots = is_array($moduleSlots['aside.tab_panel'] ?? null) ? $moduleSlots['aside.tab_panel'] : [];
$moduleSearchSlots = is_array($moduleSlots['search.resource_item'] ?? null) ? $moduleSlots['search.resource_item'] : [];
$moduleNavItemSlots = is_array($moduleSlots['sidebar.admin_nav_item'] ?? null) ? $moduleSlots['sidebar.admin_nav_item'] : [];
?>
<aside class="app-sidebar" id="app-sidebar" data-app-component="sidebar-toggle">
<?php if ($currentTenant): ?>