Add a menu destination
Publish a Resource destination and a standalone App page into the App Menu, and verify each appears for the right actor.
Publish a link to an existing App screen in the App Menu. Start with an installed App from Create an App Package and a working screen; menu metadata does not create the screen or authorize its API.
Publish the destination
Implement NavigationContribution in a class under the manifest's contribution locations. Return NavigationItem values from navigationItems(). This is a complete contribution from packages/people/src/Contribution/PeopleWorkNavigation.php, reduced to one existing destination:
<?php
namespace Amuzcorp\Nexia\PeopleCore\Contribution;
use Nexia\Navigation\Contracts\NavigationContribution;
use Nexia\Navigation\NavigationItem;
final class PeopleWorkNavigation implements NavigationContribution
{
public static function navigationItems(): array
{
return [NavigationItem::make(
id: 'people-onboarding',
route: '/apps/people/onboarding',
icon: 'list-checks',
sort: 210,
labelKey: 'people.onboarding.title',
appKey: 'people',
contextId: 'app',
groupId: 'management',
permission: 'people.onboarding_case.read',
)];
}
}Use an existing read permission and a locale catalog key. Keep the route's authorization in place: a permission on a destination filters the link, not requests to its endpoint. For a new custom screen, register its component and bind pageElementsExtras() as described in Build an App screen.
Refresh discovery after adding the class:
task artisan -- nexia-runtime:generate-app-map
task artisan -- nexia-runtime:refresh-runtime-cachesConfirm the result
With the App operational and the permission assigned, open its menu: Onboarding belongs under Management and opens the existing screen. Switch to an actor without the read permission: the link disappears and the protected API refuses the same read. Search the localized label in the Command Palette to check searchable behavior.
Resource lists and placement
A generated Resource already publishes navigation through ContributesNavigationDestination; change its $navigation declaration instead of adding a second contributor. Create a Resource owns generation. visible: false keeps the Resource routable while removing its menu entry.
| Placement | Use |
|---|---|
contextId: 'app' | Resolves to the owning app:{app_key} context |
groupId: null | Direct destination, including the App overview |
insights, management, operations, master-data, settings | Supported App groups |
subgroupId | Subdivision within a group; requires groupId |
permission array | Any listed permission makes the destination discoverable |
searchable: false | Exclude this destination from search |
The platform owns Shell contexts and group order. Use Add a settings page for configuration and Command Palette for optional agent locate actions. NavigationItem in packages/app-sdk/packages/laravel/src/Navigation/NavigationItem.php defines optional parent, active-pattern, and subject-population fields.
When an entry is missing
A discovered contribution still needs an operational App, an actor with its permission, and a registered destination component. nexia-apps:doctor-package-app diagnoses contribution and page-component wiring; it does not assign permissions. A blank screen usually means the route/component pairing is incomplete. Follow Test an App for the package-level check.