feat(modules): dynamic sidebar group rendering active modules

The sidebar gains a new section that lists each currently-activated module
as a direct shortcut to its settings page. This is the always-visible
companion to the company-context Active Modules index — both surface the
same set of modules, but the index is the catalog landing page and the
sidebar group is the per-module quick access.

- BootstrapController returns module_menu populated from
  \InvoiceShelf\Modules\Registry::allMenu(), but only on the company-context
  branch — not on the super-admin branch (lines 53-69), since super admins
  don't see the dynamic group. Because nwidart only boots service providers
  for currently-activated modules, the registry naturally contains only
  active modules at request time, no extra filtering needed.

- bootstrap.service.ts BootstrapResponse type extended with
  module_menu?: ModuleMenuItem[]; new ModuleMenuItem interface
  (title/link/icon) — shaped distinctly from MenuItem because module entries
  use namespaced i18n keys and don't carry group/ability metadata.

- global.store.ts exposes a moduleMenu ref + a hasActiveModules computed.

- SiteSidebar.vue appends a new "Modules" section after the existing
  menuGroups output, in both the mobile (Dialog) and desktop branches. The
  section is hidden when hasActiveModules is false. Uses the
  modules.sidebar.section_title i18n key added in the previous commit.
This commit is contained in:
Darko Gjorgjijoski
2026-04-09 00:29:56 +02:00
parent e6eeacb6d4
commit 7743c2e126
4 changed files with 95 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ use App\Models\Setting;
use App\Traits\GeneratesMenuTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use InvoiceShelf\Modules\Registry as ModuleRegistry;
use Silber\Bouncer\BouncerFacade;
class BootstrapController extends Controller
@@ -118,6 +119,7 @@ class BootstrapController extends Controller
'main_menu' => $main_menu,
'setting_menu' => $setting_menu,
'modules' => Module::where('enabled', true)->pluck('name'),
'module_menu' => array_values(ModuleRegistry::allMenu()),
'pending_invitations' => CompanyInvitationResource::collection($pendingInvitations),
]);
}