defmodule PhoenixKitWeb.Components.UserDashboardNav do
@moduledoc """
User dashboard navigation components for the PhoenixKit user dashboard.
Provides navigation elements specifically for user dashboard pages.
"""
use PhoenixKitWeb, :html
alias PhoenixKit.Modules.Languages
alias PhoenixKit.Modules.Languages.DialectMapper
alias PhoenixKit.Users.Auth.Scope
alias PhoenixKit.Utils.Routes
@doc """
Renders user dropdown for dashboard navigation.
Shows user avatar with dropdown menu containing email, role, settings and logout.
"""
attr(:scope, :any, default: nil)
attr(:current_path, :string, default: "")
attr(:current_locale, :string, default: "en")
attr(:admin_edit_url, :string, default: nil)
attr(:admin_edit_label, :string, default: nil)
def user_dropdown(assigns) do
user = Scope.user(assigns.scope)
assigns =
assigns
|> assign(:user, user)
~H"""
<%= if @scope && PhoenixKit.Users.Auth.Scope.authenticated?(@scope) do %>
<%= if PhoenixKit.Users.Auth.Scope.admin?(@scope) do %>
-
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin")}
class={"flex items-center gap-3" <> if(active_path?(assigns[:current_path], "/admin"), do: " bg-primary text-primary-content", else: "")}
>
<.icon name="hero-shield-check" class="w-4 h-4" />
{gettext("Admin Panel")}
<%= if @admin_edit_url do %>
-
<.icon name="hero-pencil-square" class="w-4 h-4" />
{@admin_edit_label || "Edit"}
<% end %>
<% end %>
-
<.link
navigate={PhoenixKit.Utils.Routes.path("/dashboard", locale: @current_locale)}
class={"flex items-center gap-3" <> if(active_path?(assigns[:current_path], "/dashboard"), do: " bg-primary text-primary-content", else: "")}
>
<.icon name="hero-home" class="w-4 h-4" />
Dashboard
-
<.link
navigate={PhoenixKit.Utils.Routes.path("/dashboard/settings", locale: @current_locale)}
class={"flex items-center gap-3" <> if(active_path?(assigns[:current_path], "/dashboard/settings"), do: " bg-primary text-primary-content", else: "")}
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4" />
Settings
<% user_languages = get_user_languages() %>
<%= if length(user_languages) > 1 do %>
<%!--
Independently scrollable language list — caps at ~5 visible rows.
Anchors are styled directly (no nested daisyUI
<% else %>
<.link
navigate={Routes.path("/users/log-in")}
class="btn btn-primary btn-sm"
>
Login
<% end %>
"""
end
# Helper function to get user languages from Languages module
# Returns enabled languages or falls back to English if module is disabled
defp get_user_languages do
# Get enabled languages from the Languages module
languages =
if Languages.enabled?() do
Languages.get_enabled_languages()
else
# Fallback to English when module is disabled
[%{code: "en-US", name: "English (United States)", is_enabled: true}]
end
# Map to expected format with enriched data from predefined languages
languages
|> Enum.map(fn lang ->
case Languages.get_predefined_language(lang.code) do
%{name: name, flag: flag, native: native} ->
%{code: lang.code, name: name, flag: flag, native: native}
nil ->
%{code: lang.code, name: String.upcase(lang.code), flag: "🌐", native: ""}
end
end)
end
# Helper function to get language flag emoji
defp get_language_flag(code) when is_binary(code) do
case Languages.get_predefined_language(code) do
%{flag: flag} -> flag
nil -> "🌐"
end
end
# Check if current path matches the given path
defp active_path?(current_path, path) when is_binary(current_path) and is_binary(path) do
# Remove PhoenixKit prefix if present
normalized_path = remove_phoenix_kit_prefix(current_path)
# Remove locale prefix if present
clean_path = remove_locale_prefix(normalized_path)
# Check for exact match or ends with path
clean_path == path or String.ends_with?(clean_path, path)
end
defp active_path?(_, _), do: false
# Remove PhoenixKit prefix
defp remove_phoenix_kit_prefix(path) do
url_prefix = PhoenixKit.Config.get_url_prefix()
if url_prefix == "/" do
path
else
String.replace_prefix(path, url_prefix, "")
end
end
# Remove locale prefix
defp remove_locale_prefix(path) do
case String.split(path, "/", parts: 3) do
["", locale, rest] when locale != "" and rest != "" ->
if looks_like_locale?(locale), do: "/" <> rest, else: path
["", locale] ->
if looks_like_locale?(locale), do: "/", else: path
_ ->
path
end
end
# Check if it looks like a locale code
defp looks_like_locale?(locale) do
String.length(locale) <= 8 and String.match?(locale, ~r/^[a-z]{2,3}(-[A-Za-z]{2,4})?$/)
end
# Legacy helper - kept for backward compatibility
defp generate_language_switch_url(current_path, new_locale) do
base_code = DialectMapper.extract_base(new_locale)
# Extract the path without locale and regenerate with new locale
url_prefix = PhoenixKit.Config.get_url_prefix()
base_prefix = if url_prefix === "/", do: "", else: url_prefix
# Remove prefix and locale from current_path
clean_path =
current_path
|> String.replace_prefix(base_prefix, "")
|> remove_locale_from_path()
# Generate new path with the new locale
Routes.path(clean_path, locale: base_code)
end
# Remove locale from path
defp remove_locale_from_path(path) do
case String.split(path, "/", trim: true) do
[segment | rest] when byte_size(segment) in [2, 5] ->
# Check if segment looks like a locale
if String.length(segment) == 2 or
(String.length(segment) == 5 and String.contains?(segment, "-")) do
"/" <> Path.join(rest)
else
path
end
_ ->
path
end
end
end