PhoenixKit.ProfileSettingsTab (phoenix_kit v2.64.1)

Copy Markdown View Source

A tab a host application or a module adds to the signed-in user's settings strip, /profile/settings/<id> (PhoenixKitWeb.Components.ProfileSettingsTabs).

The tab is a link to a page of its own, like core's Integrations tab: every tab of the strip navigates to it, and it navigates back. The page renders the strip itself so it looks like the other tabs:

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
  socket={@socket}
  flash={@flash}
  phoenix_kit_current_scope={@phoenix_kit_current_scope}
  page_title={gettext("My tab")}
  current_path={PhoenixKitWeb.Components.ProfileSettingsTabs.path("my-tab")}
>
  <div class="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
    <PhoenixKitWeb.Components.ProfileSettingsTabs.profile_tabs
      active="my-tab"
      scope={@phoenix_kit_current_scope}
    />
    ...
  </div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>

Declaring tabs

A host lists them in config:

config :phoenix_kit, :profile_settings_tabs, [
  %{
    id: "my-tab",
    label: {MyAppWeb.ProfileTabs, :my_tab_label, []},
    icon: "hero-puzzle-piece",
    after: "security",
    visible: {MyApp.MyFeature, :available?},
    live_view: MyAppWeb.MyTabLive
  }
]

Use the tuple forms in config: a release cannot carry a function in its config. A module returns the same maps from PhoenixKit.Module.profile_settings_tabs/0 (functions are fine there); its tabs are offered only while the module is enabled. Host tabs come first; when two declare one id, the first wins and the other is dropped with a warning.

Fields

  • :id (required) — the URL segment, /profile/settings/<id>: lowercase letters, digits, - and _, starting with a letter or digit. Not one of core's tab ids or confirm-email (reserved_ids/0).
  • :label (required) — a string, a {module, function, args} or a zero-arity function. The last two are called on every render, after the kit has set the request's Gettext locale, so they can return the host's own gettext("My tab") — a plain string is shown as is.
  • :icon — a heroicon name ("hero-puzzle-piece"), default "hero-squares-2x2".
  • :after — the id of the tab this one follows (a core tab or another registered one). Absent, or naming a tab that does not exist, puts it at the end. Tabs after the same one keep their declared order.
  • :permission — a permission key the user must hold (PhoenixKit.Users.Auth.Scope.can?/2, including its base permission and live feature-enablement checks). It must be a key the permission system knows — a module's, a core section's, or a host key registered with PhoenixKit.Users.Permissions.register_custom_key/2; an unknown key reads as a disabled feature and hides the tab.
  • :visible — {module, function} or a one-argument function, called with the scope (nil for nobody signed in); the tab is offered when it returns true. One that raises hides the tab (with one warning). With :permission too, both must pass.
  • :live_view — the page: a LiveView module or {module, action} (action :index by default). Core then declares live "/profile/settings/<id>" in its authenticated live_session, in both the locale-prefixed and the plain URL shapes, before its own /profile/settings/:tab. Without it the host declares the route ITSELF, before phoenix_kit_routes() (and in both shapes); a tab whose page is missing lands on ProfileSettings, which redirects it to the first tab.

The tab decides what the strip offers, not who may open the page: the page checks access in its own mount/3, as any page does.

Summary

Functions

The registered tabs: the host's (config :phoenix_kit, :profile_settings_tabs) followed by those of the enabled modules. Invalid and duplicate entries are dropped, each with one warning.

The host's raw config entries, as the router compares them for recompiles.

The tabs mod.profile_settings_tabs/0 declares, normalised. A callback that raises contributes nothing (one warning).

The tab's label for this render.

Normalise one entry (a map or a keyword list). Returns {:error, reason} (a string) for anything that is not a valid tab.

The ids a registered tab cannot take.

The tabs whose page core routes, for the router macros: the host's and those of every discovered module, enabled or not (routes are compiled; the strip asks enabled?/0 at render time). modules are the modules to ask, already compiled.

Whether scope is offered the tab: its :permission held and its :visible true. Fails closed — a check that raises hides the tab.

Types

label()

@type label() :: String.t() | {module(), atom(), list()} | (-> String.t())

t()

@type t() :: %PhoenixKit.ProfileSettingsTab{
  after: String.t() | nil,
  icon: String.t(),
  id: String.t(),
  label: label(),
  live_view: {module(), atom()} | nil,
  module: module() | nil,
  permission: String.t() | nil,
  visible: visible()
}

visible()

@type visible() :: {module(), atom()} | (term() -> boolean()) | nil

Functions

all()

@spec all() :: [t()]

The registered tabs: the host's (config :phoenix_kit, :profile_settings_tabs) followed by those of the enabled modules. Invalid and duplicate entries are dropped, each with one warning.

config_entries()

@spec config_entries() :: term()

The host's raw config entries, as the router compares them for recompiles.

from_module(mod)

@spec from_module(module()) :: [t()]

The tabs mod.profile_settings_tabs/0 declares, normalised. A callback that raises contributes nothing (one warning).

label(profile_settings_tab)

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

The tab's label for this render.

new(entry)

@spec new(term()) :: {:ok, t()} | {:error, String.t()}

Normalise one entry (a map or a keyword list). Returns {:error, reason} (a string) for anything that is not a valid tab.

reserved_ids()

@spec reserved_ids() :: [String.t()]

The ids a registered tab cannot take.

routed(modules)

@spec routed([module()]) :: [t()]

The tabs whose page core routes, for the router macros: the host's and those of every discovered module, enabled or not (routes are compiled; the strip asks enabled?/0 at render time). modules are the modules to ask, already compiled.

visible?(tab, scope)

@spec visible?(t(), term()) :: boolean()

Whether scope is offered the tab: its :permission held and its :visible true. Fails closed — a check that raises hides the tab.