# `PhoenixKitWeb.Components.Dashboard.AdminSidebar`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.15.1/lib/phoenix_kit_web/components/dashboard/admin_sidebar.ex#L1)

Admin sidebar component for the PhoenixKit admin panel.

Renders the admin navigation using registry-driven Tab structs instead of
hardcoded HEEX. Supports:
- Permission-gated tabs (filtered by Registry)
- Module-enabled filtering (filtered by Registry)
- Dynamic children for Entities and Publishing
- Subtab expand/collapse
- Full reuse of the TabItem component for consistent rendering

## An entry is rendered only if the visitor can open it

The menu must never offer a page that bounces the visitor on arrival, so
every entry is filtered against the SAME gates its destination enforces —
see `reachable_tabs/2`. This matters because `/admin` is the guaranteed
landing for every authenticated user: a visitor with no permissions at all
now renders this shell, where before only a permission holder could.

The registry already drops a tab whose `:permission` key the scope does not
hold (`PhoenixKit.Dashboard.Registry.get_tabs/1` → `Tab.permission_granted?/2`) and a tab whose
`:visible` function says no. Two gates it does NOT apply are added here:

  * `Scope.can_access_admin_area?/1`, the first thing
    `:phoenix_kit_ensure_admin` checks. Fail it and EVERY `/admin` page
    redirects you — including the personal ones — so the whole menu is empty.
  * `PhoenixKitWeb.Users.Auth.can_access_admin_view?/2` for an entry that
    names a `live_view:`. A tab may name a view and no permission key; the
    mount gate then treats that view as *unmapped* and admits only a scope
    holding every enabled permission, while the sidebar happily linked it for
    everyone.

## Usage

    <.admin_sidebar
      current_path={@current_path}
      scope={@phoenix_kit_current_scope}
      locale={@current_locale}
    />

# `admin_sidebar`

Renders the complete admin sidebar navigation.

## Attributes

- `current_path` - The current URL path for active state detection
- `scope` - The current authentication scope for permission filtering
- `locale` - The current locale for path generation
- `class` - Additional CSS classes

## Attributes

* `current_path` (`:string`) - Defaults to `"/admin"`.
* `scope` (`:any`) - Defaults to `nil`.
* `locale` (`:string`) - Defaults to `nil`.
* `class` (`:string`) - Defaults to `""`.

# `reachable_tabs`

```elixir
@spec reachable_tabs(
  [PhoenixKit.Dashboard.Tab.t()],
  PhoenixKit.Users.Auth.Scope.t() | nil
) :: [
  PhoenixKit.Dashboard.Tab.t()
]
```

Keeps only the entries `scope` can actually open.

Two gates, in the order the mount hook applies them:

1. `Scope.can_access_admin_area?/1` — the admin-area gate
   `:phoenix_kit_ensure_admin` checks before anything else. A scope that
   fails it (a `nil` scope, or an authenticated user holding no permission at
   all) is redirected off every `/admin` page, personal ones included, so the
   answer is `[]` — not "the tabs with no permission key".
2. `PhoenixKitWeb.Users.Auth.can_access_admin_view?/2` for any entry naming a
   `live_view:` — the same function the mount gate asks. Nothing is restated
   here, so a rendered entry and its destination cannot disagree.

An entry with no `live_view:` is left to the registry's own `:permission` /
`:visible` filtering — `PhoenixKit.Dashboard.Registry.get_admin_tabs/1` applies both before the
sidebar calls this. Every tab core ships is of that shape: core declares its
admin routes in the router rather than on the tab, so there is no module to
ask, and gate 2 is a structural no-op over core's own menu.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
