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 '@fleetbase/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:
| Registry | Holds | Registrar |
|---|---|---|
header | Header navigation bar items (top of console) | registerHeaderMenuItem |
console:account | Organization & user dropdowns (slugs prefixed organization: or user:) | registerOrganizationMenuItem, registerUserMenuItem |
console:admin | Admin section — items (menu-item) and panels (menu-panel) | registerAdminMenuItem, registerAdminMenuPanel |
console:settings | Settings page items | registerSettingsMenuItem |
auth:login | Login 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 '@fleetbase/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('@fleetbase/fleetops-engine', 'admin/routing-settings'),
}),
new MenuItem({
title: 'Map',
icon: 'map',
component: new ExtensionComponent('@fleetbase/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('@fleetbase/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('@fleetbase/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
| Method | Returns |
|---|---|
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
| Extension | Pattern shown | Source |
|---|---|---|
| Fleet-Ops | Header item with shortcuts + admin panel | fleetops/addon/extension.js |
| Storefront | Header item with shortcuts + component contributions | storefront/addon/extension.js |
| Pallet | Header item, component contributions | pallet/addon/extension.js |
| Ledger | Header item, custom dashboard, Fleet-Ops order tab | ledger/addon/extension.js |
See Also
- Registry Service — for component contributions (renderable components)
MenuItemcontract — full property tableMenuPanelcontract
Source
| File | Description |
|---|---|
addon/services/universe/menu-service.js | The MenuService implementation |
addon/contracts/menu-item.js | MenuItem |
addon/contracts/menu-panel.js | MenuPanel |