LogisBaseLogisBase

Menu Service

Register navigation entries in the console — header items, settings panels, admin panels, organization & user menus, and custom registries — via universe.getService('menu').

Menu Service

The menu service is how your extension contributes navigation entries to the console. It exposes dedicated registrars for each major menu surface, plus a generic registrar for custom registries (auth:login, engine:fleet-ops, etc.).

// addon/extension.js
import { MenuItem, ExtensionComponent } from '@logisbase/ember-core/contracts';

export default {
  setupExtension(app, universe) {
    const menuService = universe.getService('menu');
    // … registrations
  },
};

Registry Names

The service maintains a small set of well-known registries, addressed by colon-separated names:

RegistryHoldsRegistrar
headerHeader navigation bar items (top of console)registerHeaderMenuItem
console:accountOrganization & user dropdowns (slugs prefixed organization: or user:)registerOrganizationMenuItem, registerUserMenuItem
console:adminAdmin section — items (menu-item) and panels (menu-panel)registerAdminMenuItem, registerAdminMenuPanel
console:settingsSettings page itemsregisterSettingsMenuItem
auth:loginLogin screen extras (extra buttons, public unauth pages)registerMenuItem('auth:login', …)
engine:<name>Per-engine settings drawer (engine:fleet-ops, etc.)registerMenuItem('engine:<name>', …)
Custom (my-ext:tabs, etc.)Anything you define via registry.createRegistries(...)registerMenuItem('my-ext:tabs', …)

registerHeaderMenuItem(itemOrTitle, route?, options?)

The most-used registrar — adds an entry to the top header of the console.

menuService.registerHeaderMenuItem('Storefront', 'console.storefront', {
  icon: 'store',
  priority: 1,
  description: 'Online store management.',
  shortcuts: [
    {
      title: 'Products',
      icon: 'box-open',
      route: 'console.storefront.products',
    },
    {
      title: 'Orders',
      icon: 'bag-shopping',
      route: 'console.storefront.orders',
    },
    {
      title: 'Customers',
      icon: 'users',
      route: 'console.storefront.customers',
    },
  ],
});

The shortcuts array isn't decorative — each shortcut is automatically registered as a first-class header item (with the parent's title as _parentTitle for grouping in the customizer's overflow dropdown). This means a single header registration can surface multiple discoverable entries.

You can also pass a MenuItem:

import { MenuItem } from '@logisbase/ember-core/contracts';

menuService.registerHeaderMenuItem(
  new MenuItem({
    title: 'Storefront',
    route: 'console.storefront',
    icon: 'store',
    priority: 1,
  }),
);

registerSettingsMenuItem(itemOrTitle, options?)

Adds an entry to the Settings page.

menuService.registerSettingsMenuItem(
  new MenuItem({
    title: 'My Extension',
    slug: 'my-extension',
    icon: 'gear',
    component: new ExtensionComponent('@my-org/my-engine', 'settings-panel'),
  }),
);

When the MenuItem has a component, it renders inside the settings page when the user clicks the entry. If you also want a route transition, set route: 'console.settings.virtual' and use slug to identify the view.

registerAdminMenuItem(itemOrTitle, route?, options?) / registerAdminMenuPanel(panelOrTitle, items?, options?)

The admin section supports both flat items and grouped panels.

menuService.registerAdminMenuPanel(
  'Fleet-Ops Config',
  [
    new MenuItem({
      title: 'Routing',
      icon: 'route',
      component: new ExtensionComponent(
        '@logisbase/fleetops-engine',
        'admin/routing-settings',
      ),
    }),
    new MenuItem({
      title: 'Map',
      icon: 'map',
      component: new ExtensionComponent(
        '@logisbase/fleetops-engine',
        'admin/map-settings',
      ),
    }),
  ],
  { slug: 'fleet-ops' },
);

A panel is stored at console:admin → menu-panel → <slug>; its child items also fan out to console:admin → menu-item → <itemSlug> so they're discoverable individually.

registerOrganizationMenuItem(itemOrTitle, options?)

Items in the organization dropdown (top-right). Slug is auto-prefixed with organization:.

menuService.registerOrganizationMenuItem(
  new MenuItem({
    title: 'Ledger Preferences',
    icon: 'calculator',
    slug: 'ledger-preferences',
    view: 'index',
    component: new ExtensionComponent(
      '@logisbase/ledger-engine',
      'ledger-org-settings',
    ),
    onClick: (menuItem) => {
      const router = app.lookup('service:router');
      router?.transitionTo('console.settings.virtual', menuItem.slug);
    },
  }),
);

If section isn't provided, it defaults to 'settings'.

registerUserMenuItem(itemOrTitle, options?)

Items in the user dropdown. Slug is auto-prefixed with user:.

menuService.registerUserMenuItem(
  new MenuItem({
    title: 'Help & Tours',
    icon: 'question-circle',
    onClick: () => app.lookup('service:tour')?.showTourLauncher(),
  }),
);

If section isn't provided, it defaults to 'account'.

registerMenuItem(registryName, titleOrMenuItem, options?)

The generic registrar for custom registries — your own (my-ext:sidebar) or well-known engine registries (auth:login, engine:fleet-ops, fleet-ops:component:vehicle:details).

// A button on the login screen — links to your engine's own login route
menuService.registerMenuItem(
  'auth:login',
  new MenuItem({
    title: 'Partner Login',
    route: 'partner-portal.login',
    icon: 'person',
    type: 'link',
    wrapperClass:
      'btn-block py-1 border dark:border-gray-700 border-gray-200 hover:opacity-50',
  }),
);

// A tab on the Fleet-Ops order details panel
menuService.registerMenuItem(
  'fleet-ops:component:order:details',
  new MenuItem({
    title: 'Invoice',
    route: 'operations.orders.index.details.virtual',
    component: new ExtensionComponent(
      '@logisbase/ledger-engine',
      'order-invoice',
    ),
    icon: 'file-invoice-dollar',
    slug: 'invoice',
  }),
);

Real example: packages/ledger/addon/extension.js:92 — registers an "Invoice" tab on the Fleet-Ops order details panel.

For component-injecting registries (where the host renders contributed UI rather than navigating to a route), use registry.registerRenderableComponent(...) instead — it's the right tool for component contributions.

Reading Items Back

MethodReturns
getHeaderMenuItems()Array of header items
getOrganizationMenuItems()Items prefixed organization:
getUserMenuItems()Items prefixed user:
getAdminMenuItems()Admin items (excluding panel children)
getAdminMenuPanels() (alias getAdminPanels)Admin panels
getSettingsMenuItems()Settings page items
getSettingsMenuPanels()Settings panels
getMenuItems(registryName)Items in any registry
getMenuPanels(registryName)Panels in any registry
getMenuItemsFromPanel(panelSlug)Items inside a specific panel
lookupMenuItem(registryName, slug, view?, section?) (alias getMenuItem)A specific item

Permission Gating

Pass permission on a MenuItem and the menu UI will hide or disable the item per the user's IAM abilities.

new MenuItem({
  title: 'Delete Driver',
  icon: 'trash',
  permission: 'fleet-ops driver delete',
  onClick: deleteDriver,
});

Real-World References

ExtensionPattern shownSource
Fleet-OpsHeader item with shortcuts + admin panelfleetops/addon/extension.js
StorefrontHeader item with shortcuts + component contributionsstorefront/addon/extension.js
PalletHeader item, component contributionspallet/addon/extension.js
LedgerHeader item, custom dashboard, Fleet-Ops order tabledger/addon/extension.js

See Also

Source

Menu Service | LogisBase