Search documentation of phoenix_kit

# PhoenixKit.Dashboard (phoenix_kit v1.7.87)

Copy Markdown View Source

User Dashboard Tab Management System.

PhoenixKit's dashboard provides a flexible, extensible navigation system that parent applications can customize with their own tabs, badges, and features.

## Features

- Dynamic Tabs: Register tabs from config or at runtime
- Live Badges: Real-time badge updates via PubSub
- Grouping: Organize tabs into logical sections with headers
- Conditional Visibility: Show/hide tabs based on roles or custom logic
- Attention Indicators: Pulse, bounce, shake animations to draw attention
- Presence Tracking: Show how many users are viewing each tab
- Path Matching: Flexible active state detection (exact, prefix, custom)

## Quick Start

### 1. Configure Tabs in config.exs

    config :phoenix_kit, :user_dashboard_tabs, [
      %{
        id: :orders,
        label: "My Orders",
        icon: "hero-shopping-bag",
        path: "/dashboard/orders",
        priority: 100
      },
      %{
        id: :notifications,
        label: "Notifications",
        icon: "hero-bell",
        path: "/dashboard/notifications",
        priority: 200,
        badge: %{type: :count, value: 0, color: :error}
      }
    ]

### 2. Register Tabs at Runtime (Optional)

    # In your application startup or a LiveView mount
    PhoenixKit.Dashboard.register_tabs(:my_app, [
      %{
        id: :printers,
        label: "Printers",
        icon: "hero-cube",
        path: "/dashboard/printers",
        priority: 150,
        badge: %{
          type: :count,
          subscribe: {"farm:stats", fn msg -> msg.printing_count end}
        }
      }
    ])

### 3. Update Badges Live

    # From anywhere in your app
    PhoenixKit.Dashboard.update_badge(:notifications, 5)
    PhoenixKit.Dashboard.update_badge(:printers, count: 3, color: :warning)

### 4. Trigger Attention

    # Make a tab pulse to draw attention
    PhoenixKit.Dashboard.set_attention(:alerts, :pulse)

## Tab Groups

Organize tabs into sections:

    config :phoenix_kit, :user_dashboard_tab_groups, [
      %{id: :main, label: nil, priority: 100},
      %{id: :farm, label: "Farm Management", priority: 200, icon: "hero-cube"},
      %{id: :account, label: "Account", priority: 900}
    ]

Then assign tabs to groups:

    %{id: :printers, label: "Printers", path: "/dashboard/printers", group: :farm}

## Conditional Visibility

Use `visible` for non-permission conditional logic (feature flags, user data). For access control, use the `permission` field instead.

    %{
      id: :beta_feature,
      label: "Beta",
      path: "/dashboard/beta",
      visible: fn scope ->
        scope.user.features["beta_enabled"] == true
      end
    }

## Live Badges with PubSub

Badges can subscribe to PubSub topics for real-time updates:

    %{
      id: :notifications,
      label: "Notifications",
      path: "/dashboard/notifications",
      badge: %{
        type: :count,
        color: :error,
        subscribe: {"user:#{user_uuid}:notifications", :unread_count}
      }
    }

When a message is broadcast to the topic, the badge automatically updates.

## Presence Tracking

Track which users are viewing which tabs:

    # In your LiveView
    def mount(_params, _session, socket) do
      if connected?(socket) do
        PhoenixKit.Dashboard.Presence.track_tab(socket, :orders)
      end
      {:ok, socket}
    end

The sidebar will show "2 viewing" indicators.

## Summary

### Functions

clear_attention(tab_id)

Clears attention animation from a tab.

clear_badge(tab_id)

Clears a tab's badge.

context_selector_enabled?()

Checks if the context selector feature is enabled.

count_badge(value, opts \\ [])

Creates a count badge.

current_context(arg1)

Gets the current context from socket assigns.

current_context_uuid(socket_or_assigns)

Gets the current context ID from socket assigns.

decrement_badge(tab_id, amount \\ 1)

Decrements a tab's count badge by a given amount.

divider(opts \\ [])

Creates a divider for visual separation in the sidebar.

dot_badge(opts \\ [])

Creates a dot badge.

get_admin_tabs(opts \\ [])

Gets all admin-level tabs, filtered by permission and module-enabled status.

get_all_viewer_counts()

Gets viewer counts for all tabs.

get_groups()

Gets all registered tab groups.

get_subtabs(parent_id, opts \\ [])

Gets all subtabs for a given parent tab ID.

get_tab(tab_id)

Gets a specific tab by ID.

get_tabs(opts \\ [])

Gets all registered tabs, sorted by priority.

get_tabs_with_active(current_path, opts \\ [])

Gets all tabs with their active state for the given path.

get_top_level_tabs(opts \\ [])

Gets only top-level tabs (tabs without a parent).

get_user_tabs(opts \\ [])

Gets all user-level tabs, filtered by visibility and scope.

get_viewer_count(tab_id)

Gets the number of users viewing a specific tab.

group_header(opts)

Creates a group header for organizing tabs.

has_multiple_contexts?(arg1)

Checks if the user has multiple contexts available.

has_subtabs?(tab_id)

Checks if a tab has any subtabs.

increment_badge(tab_id, amount \\ 1)

Increments a tab's count badge by a given amount.

live_badge(topic, extractor, opts \\ [])

Creates a live badge that subscribes to PubSub updates.

load_admin_defaults()

Loads the default admin tabs into the registry.

matches_path?(tab, path)

Checks if a tab matches the given path.

new_badge(attrs)

Creates a new Badge struct.

new_tab(attrs)

Creates a new Tab struct.

new_tab!(attrs)

Creates a new Tab struct, raising on error.

pubsub_topic()

Gets the PubSub topic for tab updates.

register_admin_tabs(namespace, tabs)

Registers admin tabs for an application namespace.

register_groups(groups)

Registers tab groups for organizing the sidebar.

register_tabs(namespace, tabs)

Registers dashboard tabs for an application namespace.

set_attention(tab_id, animation)

Sets an attention animation on a tab.

show_subtabs?(tab, active)

Checks if subtabs should be shown for a tab based on its display setting and active state.

status_badge(value, opts \\ [])

Creates a status badge.

subscribe()

Subscribes the current process to tab updates.

subtab?(tab)

Checks if a tab is a subtab (has a parent).

track_presence(socket, tab_id, opts \\ [])

Tracks a user's presence on a dashboard tab.

unregister_tab(tab_id)

Unregisters a specific tab by ID.

unregister_tabs(namespace)

Unregisters all tabs for a namespace.

update_badge(tab_id, value)

Updates a tab's badge.

update_tab(tab_id, attrs)

Updates an existing tab's attributes by ID.

visible?(tab, scope)

Checks if a tab is visible for the given scope.

## Functions

# clear_attention(tab_id)

    @spec clear_attention(atom()) :: :ok

Clears attention animation from a tab.

# clear_badge(tab_id)

    @spec clear_badge(atom()) :: :ok

Clears a tab's badge.

# context_selector_enabled?()

    @spec context_selector_enabled?() :: boolean()

Checks if the context selector feature is enabled.

## Examples

    if PhoenixKit.Dashboard.context_selector_enabled?() do
      # Context switching is available
    end

# count_badge(value, opts \\ [])

    @spec count_badge(
      integer(),
      keyword()
    ) :: PhoenixKit.Dashboard.Badge.t()

Creates a count badge.

# current_context(arg1)

    @spec current_context(Phoenix.LiveView.Socket.t() | map()) :: any() | nil

Gets the current context from socket assigns.

Returns nil if context selector is not configured or no context is selected.

## Examples

    context = PhoenixKit.Dashboard.current_context(socket)
    # => %MyApp.Farm{id: 1, name: "My Farm"}

    context = PhoenixKit.Dashboard.current_context(socket.assigns)
    # => %MyApp.Farm{id: 1, name: "My Farm"}

# current_context_uuid(socket_or_assigns)

    @spec current_context_uuid(Phoenix.LiveView.Socket.t() | map()) :: any() | nil

Gets the current context ID from socket assigns.

Convenience function that extracts just the ID.

## Examples

    context_uuid = PhoenixKit.Dashboard.current_context_uuid(socket)
    # => "550e8400-e29b-41d4-a716-446655440000"

# decrement_badge(tab_id, amount \\ 1)

    @spec decrement_badge(atom(), integer()) :: :ok

Decrements a tab's count badge by a given amount.

Will not go below 0.

# divider(opts \\ [])

    @spec divider(keyword()) :: PhoenixKit.Dashboard.Tab.t()

Creates a divider for visual separation in the sidebar.

## Examples

    PhoenixKit.Dashboard.divider(priority: 150)
    PhoenixKit.Dashboard.divider(priority: 200, label: "Account")

# dot_badge(opts \\ [])

    @spec dot_badge(keyword()) :: PhoenixKit.Dashboard.Badge.t()

Creates a dot badge.

# get_admin_tabs(opts \\ [])

    @spec get_admin_tabs(keyword()) :: [PhoenixKit.Dashboard.Tab.t()]

Gets all admin-level tabs, filtered by permission and module-enabled status.

## Options

- `:scope` - The current authentication scope for permission filtering

## Examples

    tabs = PhoenixKit.Dashboard.get_admin_tabs(scope: scope)

# get_all_viewer_counts()

    @spec get_all_viewer_counts() :: map()

Gets viewer counts for all tabs.

## Examples

    counts = PhoenixKit.Dashboard.get_all_viewer_counts()
    # => %{orders: 3, settings: 1, printers: 2}

# get_groups()

    @spec get_groups() :: [PhoenixKit.Dashboard.Group.t()]

Gets all registered tab groups.

# get_subtabs(parent_id, opts \\ [])

    @spec get_subtabs(
      atom(),
      keyword()
    ) :: [PhoenixKit.Dashboard.Tab.t()]

Gets all subtabs for a given parent tab ID.

## Examples

    PhoenixKit.Dashboard.get_subtabs(:orders)
    # => [%Tab{id: :pending_orders, parent: :orders, ...}, ...]

# get_tab(tab_id)

    @spec get_tab(atom()) :: PhoenixKit.Dashboard.Tab.t() | nil

Gets a specific tab by ID.

## Examples

    tab = PhoenixKit.Dashboard.get_tab(:orders)

# get_tabs(opts \\ [])

    @spec get_tabs(keyword()) :: [PhoenixKit.Dashboard.Tab.t()]

Gets all registered tabs, sorted by priority.

## Options

- `:scope` - Filter by visibility using the current scope
- `:include_hidden` - Include tabs that would be hidden (default: false)

## Examples

    tabs = PhoenixKit.Dashboard.get_tabs()
    tabs = PhoenixKit.Dashboard.get_tabs(scope: socket.assigns.phoenix_kit_current_scope)

# get_tabs_with_active(current_path, opts \\ [])

    @spec get_tabs_with_active(
      String.t(),
      keyword()
    ) :: [map()]

Gets all tabs with their active state for the given path.

Returns tabs with an additional `:active` key.

## Examples

    tabs = PhoenixKit.Dashboard.get_tabs_with_active("/dashboard/orders")

# get_top_level_tabs(opts \\ [])

    @spec get_top_level_tabs(keyword()) :: [PhoenixKit.Dashboard.Tab.t()]

Gets only top-level tabs (tabs without a parent).

## Examples

    PhoenixKit.Dashboard.get_top_level_tabs()
    # => [%Tab{id: :orders, parent: nil, ...}, ...]

# get_user_tabs(opts \\ [])

    @spec get_user_tabs(keyword()) :: [PhoenixKit.Dashboard.Tab.t()]

Gets all user-level tabs, filtered by visibility and scope.

## Options

- `:scope` - The current authentication scope for visibility filtering

## Examples

    tabs = PhoenixKit.Dashboard.get_user_tabs(scope: scope)

# get_viewer_count(tab_id)

    @spec get_viewer_count(atom()) :: integer()

Gets the number of users viewing a specific tab.

## Examples

    count = PhoenixKit.Dashboard.get_viewer_count(:orders)

# group_header(opts)

    @spec group_header(keyword()) :: PhoenixKit.Dashboard.Tab.t()

Creates a group header for organizing tabs.

## Examples

    PhoenixKit.Dashboard.group_header(id: :farm, label: "Farm Management", priority: 200)

# has_multiple_contexts?(arg1)

    @spec has_multiple_contexts?(Phoenix.LiveView.Socket.t() | map()) :: boolean()

Checks if the user has multiple contexts available.

Returns true only if context selector is enabled and user has 2+ contexts.

## Examples

    if PhoenixKit.Dashboard.has_multiple_contexts?(socket) do
      # Show context-specific UI
    end

# has_subtabs?(tab_id)

    @spec has_subtabs?(atom()) :: boolean()

Checks if a tab has any subtabs.

## Examples

    PhoenixKit.Dashboard.has_subtabs?(:orders)
    # => true

# increment_badge(tab_id, amount \\ 1)

    @spec increment_badge(atom(), integer()) :: :ok

Increments a tab's count badge by a given amount.

## Examples

    PhoenixKit.Dashboard.increment_badge(:notifications)
    PhoenixKit.Dashboard.increment_badge(:notifications, 5)

# live_badge(topic, extractor, opts \\ [])

    @spec live_badge(String.t(), atom() | (map() -> any()), keyword()) ::
      PhoenixKit.Dashboard.Badge.t()

Creates a live badge that subscribes to PubSub updates.

# load_admin_defaults()

    @spec load_admin_defaults() :: :ok

Loads the default admin tabs into the registry.

Called automatically on Registry startup, but can be called manually to reload defaults after changes.

# matches_path?(tab, path)

    @spec matches_path?(PhoenixKit.Dashboard.Tab.t(), String.t()) :: boolean()

Checks if a tab matches the given path.

# new_badge(attrs)

    @spec new_badge(map() | keyword()) ::
      {:ok, PhoenixKit.Dashboard.Badge.t()} | {:error, String.t()}

Creates a new Badge struct.

See `PhoenixKit.Dashboard.Badge.new/1` for options.

# new_tab(attrs)

    @spec new_tab(map() | keyword()) ::
      {:ok, PhoenixKit.Dashboard.Tab.t()} | {:error, String.t()}

Creates a new Tab struct.

See `PhoenixKit.Dashboard.Tab.new/1` for options.

# new_tab!(attrs)

    @spec new_tab!(map() | keyword()) :: PhoenixKit.Dashboard.Tab.t()

Creates a new Tab struct, raising on error.

# pubsub_topic()

    @spec pubsub_topic() :: String.t()

Gets the PubSub topic for tab updates.

Subscribe to this topic to receive real-time tab updates in LiveViews.

## Example

    def mount(_params, _session, socket) do
      if connected?(socket) do
        Phoenix.PubSub.subscribe(PubSubHelper.pubsub(), PhoenixKit.Dashboard.pubsub_topic())
      end
      {:ok, socket}
    end

    def handle_info({:tab_updated, tab}, socket) do
      # Handle tab update - refresh sidebar
      {:noreply, assign(socket, tabs: PhoenixKit.Dashboard.get_tabs())}
    end

    def handle_info(:tabs_refreshed, socket) do
      # Full tab list refresh
      {:noreply, assign(socket, tabs: PhoenixKit.Dashboard.get_tabs())}
    end

# register_admin_tabs(namespace, tabs)

    @spec register_admin_tabs(atom(), [map() | PhoenixKit.Dashboard.Tab.t()]) ::
      :ok | {:error, term()}

Registers admin tabs for an application namespace.

Automatically sets `level: :admin` on all tabs.

## Examples

    PhoenixKit.Dashboard.register_admin_tabs(:my_app, [
      %{id: :admin_analytics, label: "Analytics", path: "/admin/analytics",
        icon: "hero-chart-bar", permission: "dashboard"}
    ])

# register_groups(groups)

    @spec register_groups([PhoenixKit.Dashboard.Group.t() | map()]) :: :ok

Registers tab groups for organizing the sidebar.

## Examples

    PhoenixKit.Dashboard.register_groups([
      %{id: :main, label: nil, priority: 100},
      %{id: :farm, label: "Farm Management", priority: 200, icon: "hero-cube"},
      %{id: :account, label: "Account", priority: 900}
    ])

# register_tabs(namespace, tabs)

    @spec register_tabs(atom(), [map() | PhoenixKit.Dashboard.Tab.t()]) ::
      :ok | {:error, term()}

Registers dashboard tabs for an application namespace.

## Examples

    # Register multiple tabs
    PhoenixKit.Dashboard.register_tabs(:my_app, [
      %{id: :orders, label: "Orders", path: "/dashboard/orders", icon: "hero-shopping-bag"},
      %{id: :history, label: "History", path: "/dashboard/history", icon: "hero-clock"}
    ])

    # Register a single tab
    PhoenixKit.Dashboard.register_tabs(:my_app, [
      Tab.new!(id: :custom, label: "Custom", path: "/dashboard/custom")
    ])

# set_attention(tab_id, animation)

    @spec set_attention(atom(), atom()) :: :ok

Sets an attention animation on a tab.

## Animation Types

- `:pulse` - Gentle pulsing glow
- `:bounce` - Bouncing motion
- `:shake` - Shaking motion (for errors/alerts)
- `:glow` - Glowing effect

## Examples

    PhoenixKit.Dashboard.set_attention(:alerts, :pulse)
    PhoenixKit.Dashboard.set_attention(:errors, :shake)

# show_subtabs?(tab, active)

    @spec show_subtabs?(PhoenixKit.Dashboard.Tab.t(), boolean()) :: boolean()

Checks if subtabs should be shown for a tab based on its display setting and active state.

## Examples

    PhoenixKit.Dashboard.show_subtabs?(tab, true)  # parent is active
    # => true (for :when_active or :always)

    PhoenixKit.Dashboard.show_subtabs?(tab, false)  # parent not active
    # => true (only for :always)

# status_badge(value, opts \\ [])

    @spec status_badge(
      atom() | String.t(),
      keyword()
    ) :: PhoenixKit.Dashboard.Badge.t()

Creates a status badge.

# subscribe()

    @spec subscribe() :: :ok | {:error, term()}

Subscribes the current process to tab updates.

Convenience wrapper around Phoenix.PubSub.subscribe/2.

# subtab?(tab)

    @spec subtab?(PhoenixKit.Dashboard.Tab.t()) :: boolean()

Checks if a tab is a subtab (has a parent).

## Examples

    PhoenixKit.Dashboard.subtab?(:pending_orders)
    # => true

# track_presence(socket, tab_id, opts \\ [])

    @spec track_presence(Phoenix.LiveView.Socket.t(), atom(), keyword()) ::
      {:ok, String.t()} | {:error, term()}

Tracks a user's presence on a dashboard tab.

## Examples

    PhoenixKit.Dashboard.track_presence(socket, :orders)

# unregister_tab(tab_id)

    @spec unregister_tab(atom()) :: :ok

Unregisters a specific tab by ID.

## Examples

    PhoenixKit.Dashboard.unregister_tab(:orders)

# unregister_tabs(namespace)

    @spec unregister_tabs(atom()) :: :ok

Unregisters all tabs for a namespace.

## Examples

    PhoenixKit.Dashboard.unregister_tabs(:my_app)

# update_badge(tab_id, value)

    @spec update_badge(
      atom(),
      integer() | map() | keyword() | PhoenixKit.Dashboard.Badge.t() | nil
    ) :: :ok

Updates a tab's badge.

## Examples

    # Set a count badge
    PhoenixKit.Dashboard.update_badge(:notifications, 5)

    # Set badge with options
    PhoenixKit.Dashboard.update_badge(:alerts, count: 3, color: :error, pulse: true)

    # Set a dot badge
    PhoenixKit.Dashboard.update_badge(:status, type: :dot, color: :success)

    # Clear a badge
    PhoenixKit.Dashboard.update_badge(:notifications, nil)

# update_tab(tab_id, attrs)

    @spec update_tab(atom(), map()) :: :ok | {:error, :not_found}

Updates an existing tab's attributes by ID.

## Examples

    PhoenixKit.Dashboard.update_tab(:admin_dashboard, %{label: "Home", icon: "hero-home"})

# visible?(tab, scope)

    @spec visible?(PhoenixKit.Dashboard.Tab.t(), map()) :: boolean()

Checks if a tab is visible for the given scope.