feat(quick-261016-s8x): pact.NavigationItem.Visible hides navigation entries at request time

- pact.NavigationItem gains Visible func(ctx) bool (json:"-"); nil keeps the
  entry always listed
- cabana.Registry.Metadata runs it after the permission check, drops a main
  item (with its side menu) or a side item that answers false, and relinks a
  parent whose controller is a hidden child's to its first visible openable
  child
- TestNavigationHidesInvisibleItemsAndRepointsTarget covers the rules
- pact and cabana READMEs, users-and-permissions and admin-controllers docs
  describe the hook
This commit is contained in:
Jakub Zych
2026-10-16 20:34:26 +02:00
parent 90b2d4e23b
commit 06e2e60ce1
7 changed files with 113 additions and 14 deletions

View File

@@ -537,7 +537,10 @@ type HasTokenScopes interface {
}
// NavigationItem is one registerNavigation() entry. Controller is the admin
// controller ID; the SPA derives its route from that ID.
// controller ID; the SPA derives its route from that ID. Every field but
// Visible is static and compiled once at boot; Visible is the one hook the
// admin runs per navigation request, so a plugin can hide a screen whose
// feature is turned off in its settings without a restart.
type NavigationItem struct {
Code string
Label string
@@ -546,6 +549,14 @@ type NavigationItem struct {
Order int
Controller string
SideMenu []NavigationItem
// Visible decides, per request, whether the entry is listed. Nil means
// always listed. When set, the admin (cabana) calls it on every
// navigation request with the request context, after the permission
// check has passed, and leaves the entry out when it answers false; a
// main item hidden this way takes its whole side menu with it. The func
// must be cheap, must not panic, and should answer true when it cannot
// decide (no database yet, a load error).
Visible func(ctx context.Context) bool `json:"-"`
}
// HasNavigation is implemented by plugins that declare backend navigation.