Packages
phoenix_kit
1.7.192
1.7.210
1.7.209
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit_web/live/users/user_details.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
socket={@socket}
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={@page_title}
page_section={@page_section}
page_section_path={@page_section_path}
current_path={@url_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="container flex-col mx-auto px-4 py-6">
<.admin_page_header back={PhoenixKit.Utils.Routes.path("/admin/users")}>
<h1 class="text-xl sm:text-2xl lg:text-3xl font-bold text-base-content break-words min-w-0">
{@page_title}
</h1>
<p class="text-sm text-base-content/60 mt-0.5 break-words">{@user.email}</p>
<:actions>
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/users/edit/#{@user.uuid}?return_to=view")
}
class="btn btn-primary btn-sm"
>
<PhoenixKitWeb.Components.Core.Icons.icon_edit /> {gettext("Edit")}
</.link>
<%!-- Same actions offered by the "..." menu on the Users list, so nothing
there is unreachable from this page. --%>
<.table_row_menu id="user-detail-actions" label={gettext("More actions")}>
<%= if @user.uuid != @phoenix_kit_current_user.uuid do %>
<.table_row_menu_button
phx-click="show_role_management"
icon="hero-user-group"
label={gettext("Roles")}
variant="primary"
/>
<% else %>
<.table_row_menu_link
navigate={PhoenixKit.Utils.Routes.path("/dashboard/settings")}
icon="hero-cog-6-tooth"
label={gettext("Settings")}
variant="info"
/>
<% end %>
<%= if @user.uuid != @phoenix_kit_current_user.uuid && !target_is_owner?(@user) do %>
<.table_row_menu_divider />
<.table_row_menu_button
phx-click="request_confirmation_toggle"
phx-value-is_confirmed={to_string(!!@user.confirmed_at)}
icon={if @user.confirmed_at, do: "hero-envelope-open", else: "hero-envelope"}
label={
if @user.confirmed_at,
do: gettext("Unconfirm Email"),
else: gettext("Confirm Email")
}
variant={if @user.confirmed_at, do: "success", else: "warning"}
/>
<.table_row_menu_button
phx-click="request_status_toggle"
phx-value-is_active={to_string(@user.is_active)}
icon={if @user.is_active, do: "hero-check-circle", else: "hero-x-circle"}
label={if @user.is_active, do: gettext("Deactivate"), else: gettext("Activate")}
variant={if @user.is_active, do: "success", else: "warning"}
/>
<% end %>
</.table_row_menu>
<%= if Auth.can_delete_user?(@user, @phoenix_kit_current_user) do %>
<button
type="button"
class="btn btn-error btn-sm btn-outline"
phx-click="show_delete_modal"
>
<PhoenixKitWeb.Components.Core.Icons.icon_trash /> {gettext("Delete")}
</button>
<% end %>
</:actions>
</.admin_page_header>
<%!-- Tabs Navigation --%>
<div class="flex justify-center mb-6">
<div role="tablist" class="tabs tabs-boxed bg-base-200 p-1">
<button
type="button"
role="tab"
class={"tab gap-2 #{if @active_tab == "profile", do: "tab-active"}"}
phx-click="change_tab"
phx-value-tab="profile"
>
<.icon name="hero-user" class="w-4 h-4" />
{gettext("Profile")}
</button>
<%= if @connections_enabled do %>
<button
type="button"
role="tab"
class={"tab gap-2 #{if @active_tab == "connections", do: "tab-active"}"}
phx-click="change_tab"
phx-value-tab="connections"
>
<.icon name="hero-user-group" class="w-4 h-4" />
{gettext("Connections")}
</button>
<% end %>
<button
type="button"
role="tab"
class={"tab gap-2 #{if @active_tab == "notes", do: "tab-active"}"}
phx-click="change_tab"
phx-value-tab="notes"
>
<.icon name="hero-document-text" class="w-4 h-4" />
{gettext("Notes")}
<%= if length(@admin_notes) > 0 do %>
<span class="badge badge-sm h-auto">{length(@admin_notes)}</span>
<% end %>
</button>
<%= if @org_accounts_enabled && @user.account_type == "organization" do %>
<button
type="button"
role="tab"
class={"tab gap-2 #{if @active_tab == "members", do: "tab-active"}"}
phx-click="change_tab"
phx-value-tab="members"
>
<.icon name="hero-user-group" class="w-4 h-4" />
{gettext("Members")}
<%= if length(@organization_members) > 0 do %>
<span class="badge badge-sm h-auto">{length(@organization_members)}</span>
<% end %>
</button>
<% end %>
</div>
</div>
<%!-- Tab Content --%>
<div class="max-w-4xl mx-auto">
<%= if @active_tab == "profile" do %>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<%!-- Left Column: Avatar and Quick Stats --%>
<div class="lg:col-span-1">
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center">
<%!-- Avatar --%>
<div class="mb-4">
<.user_avatar user={@user} size="lg" class="!w-24 !h-24 !text-3xl" />
</div>
<%!-- Name (block, not card-title's flex, so a long unbreakable
name wraps within the card instead of overflowing both sides). --%>
<h2 class="text-xl font-semibold break-words w-full">
{user_display_name(@user)}
</h2>
<%!-- Username --%>
<%= if @user.username do %>
<p class="text-base-content/70 break-words w-full">@{@user.username}</p>
<% end %>
<%!-- Status Badges --%>
<div class="flex flex-wrap gap-2 mt-4 justify-center">
<%= if @user.is_active do %>
<span class="badge badge-success">{gettext("Active")}</span>
<% else %>
<span class="badge badge-error">{gettext("Inactive")}</span>
<% end %>
<%= if @user.confirmed_at do %>
<span class="badge badge-info">{gettext("Email Confirmed")}</span>
<% else %>
<span class="badge badge-warning">{gettext("Email Unconfirmed")}</span>
<% end %>
<%= if @org_accounts_enabled do %>
<span class={
"badge #{if @user.account_type == "organization", do: "badge-primary", else: "badge-ghost"}"
}>
{if @user.account_type == "organization",
do: gettext("Organization"),
else: gettext("Person")}
</span>
<% end %>
</div>
<%!-- Roles --%>
<%= if @user.roles && length(@user.roles) > 0 do %>
<div class="mt-4">
<h3 class="text-sm font-semibold text-base-content/70 mb-2">
{gettext("Roles")}
</h3>
<div class="flex flex-wrap gap-1 justify-center">
<%= for role <- @user.roles do %>
<.role_badge role={role} size={:sm} />
<% end %>
</div>
</div>
<% end %>
<%!-- Organization link (for person accounts) --%>
<%= if @org_accounts_enabled && @user.organization_uuid do %>
<div class="mt-4">
<h3 class="text-sm font-semibold text-base-content/70 mb-2">
{gettext("Organization")}
</h3>
<.pk_link
navigate={"/admin/users/view/#{@user.organization_uuid}"}
class="link link-primary text-sm"
>
{(Ecto.assoc_loaded?(@user.organization) && @user.organization &&
PhoenixKit.Users.Auth.User.full_name(@user.organization)) ||
to_string(@user.organization_uuid)}
</.pk_link>
</div>
<% end %>
</div>
</div>
</div>
<%!-- Right Column: Details --%>
<div class="lg:col-span-2 space-y-6">
<%!-- Basic Information --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">{gettext("Basic Information")}</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
<%= if @user.account_type == "organization" do %>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Account Type")}
</label>
<p class="text-base-content">
<span class="badge badge-info badge-sm h-auto">
{gettext("Organization")}
</span>
</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Organization Name")}
</label>
<p class="text-base-content">{@user.organization_name || "-"}</p>
</div>
<% else %>
<%= if @user.organization_uuid do %>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Organization")}
</label>
<p class="text-base-content font-mono text-sm">
{@user.organization_uuid}
</p>
</div>
<% end %>
<% end %>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Email")}
</label>
<p class="text-base-content flex items-center gap-2 flex-wrap">
{@user.email}
<%= if @user.confirmed_at do %>
<span class="badge badge-success badge-sm h-auto">
{gettext("Email Confirmed")}
</span>
<% else %>
<span class="badge badge-warning badge-sm h-auto">
{gettext("Email Unconfirmed")}
</span>
<% end %>
</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Username")}
</label>
<p class="text-base-content">{@user.username || "-"}</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("First Name")}
</label>
<p class="text-base-content">{@user.first_name || "-"}</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Last Name")}
</label>
<p class="text-base-content">{@user.last_name || "-"}</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Timezone")}
</label>
<p class="text-base-content">{format_timezone(@user.user_timezone)}</p>
</div>
</div>
</div>
</div>
<%!-- Account Information --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">{gettext("Account Information")}</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("User UUID")}
</label>
<p class="text-base-content font-mono text-sm">{@user.uuid}</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Status")}
</label>
<p class="text-base-content">
{if @user.is_active, do: gettext("Active"), else: gettext("Inactive")}
</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Email Confirmed")}
</label>
<p class="text-base-content">
<%= if @user.confirmed_at do %>
{format_datetime(
@user.confirmed_at,
@phoenix_kit_current_user,
@date_time_settings
)}
<% else %>
{gettext("Not confirmed")}
<% end %>
</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Registered")}
</label>
<p class="text-base-content">
{format_datetime(
@user.inserted_at,
@phoenix_kit_current_user,
@date_time_settings
)}
</p>
</div>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Last Updated")}
</label>
<p class="text-base-content">
{format_datetime(
@user.updated_at,
@phoenix_kit_current_user,
@date_time_settings
)}
</p>
</div>
</div>
</div>
</div>
<%!-- Registration Details --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">{gettext("Registration Details")}</h3>
<%= cond do %>
<% !@geolocation_tracking_enabled -> %>
<div class="mt-2 text-sm text-base-content/60">
{gettext(
"Location tracking is off, so this user has no registration location."
)}
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/users")}
class="link link-primary"
>
{gettext("Turn it on in Settings > Users.")}
</.link>
</div>
<% @user.registration_ip || format_location(@user) -> %>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
<%= if @user.registration_ip do %>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Registration IP")}
</label>
<p class="text-base-content font-mono text-sm">
{@user.registration_ip}
</p>
</div>
<% end %>
<%= if format_location(@user) do %>
<div>
<label class="text-sm font-medium text-base-content/70">
{gettext("Registration Location")}
</label>
<p class="text-base-content">{format_location(@user)}</p>
</div>
<% end %>
</div>
<% true -> %>
<p class="mt-2 text-sm text-base-content/50">{gettext("No data")}</p>
<% end %>
</div>
</div>
<%!-- Custom Fields --%>
<%= if @custom_field_definitions && length(@custom_field_definitions) > 0 do %>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">{gettext("Custom Fields")}</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
<%= for field <- @custom_field_definitions do %>
<div>
<label class="text-sm font-medium text-base-content/70">
{field["label"]}
</label>
<p class="text-base-content">
{format_custom_field_value(
get_custom_field_value(@user, field["key"]),
field["type"],
field["key"]
)}
</p>
</div>
<% end %>
</div>
</div>
</div>
<% else %>
<%= if @user.custom_fields && map_size(@user.custom_fields) > 0 do %>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">{gettext("Custom Fields")}</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
<%= for {key, value} <- Enum.sort(@user.custom_fields) do %>
<div>
<label class="text-sm font-medium text-base-content/70">
{Phoenix.Naming.humanize(key)}
</label>
<p class="text-base-content">
{format_custom_field_value(value, nil, key)}
</p>
</div>
<% end %>
</div>
</div>
</div>
<% end %>
<% end %>
</div>
</div>
<% end %>
<%!-- Connections Tab Content --%>
<%= if @active_tab == "connections" and @connections_enabled do %>
<div class="grid grid-cols-2 md:grid-cols-5 gap-4">
<%!-- Followers --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center p-4">
<.icon name="hero-users" class="w-8 h-8 text-primary" />
<div class="text-2xl font-bold">{@connections_stats.followers}</div>
<div class="text-sm text-base-content/70">{gettext("Followers")}</div>
</div>
</div>
<%!-- Following --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center p-4">
<.icon name="hero-user-plus" class="w-8 h-8 text-secondary" />
<div class="text-2xl font-bold">{@connections_stats.following}</div>
<div class="text-sm text-base-content/70">{gettext("Following")}</div>
</div>
</div>
<%!-- Connections --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center p-4">
<.icon name="hero-link" class="w-8 h-8 text-accent" />
<div class="text-2xl font-bold">{@connections_stats.connections}</div>
<div class="text-sm text-base-content/70">{gettext("Connections")}</div>
</div>
</div>
<%!-- Pending --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center p-4">
<.icon name="hero-clock" class="w-8 h-8 text-warning" />
<div class="text-2xl font-bold">{@connections_stats.pending}</div>
<div class="text-sm text-base-content/70">{gettext("Pending")}</div>
</div>
</div>
<%!-- Blocked --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center p-4">
<.icon name="hero-no-symbol" class="w-8 h-8 text-error" />
<div class="text-2xl font-bold">{@connections_stats.blocked}</div>
<div class="text-sm text-base-content/70">{gettext("Blocked")}</div>
</div>
</div>
</div>
<% end %>
<%!-- Notes Tab Content --%>
<%= if @active_tab == "notes" do %>
<div class="space-y-6">
<%!-- Add Note Form --%>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">
<%= if @editing_note_uuid do %>
{gettext("Edit Note")}
<% else %>
{gettext("Add Note")}
<% end %>
</h3>
<.form
for={@note_form}
phx-submit={if @editing_note_uuid, do: "update_note", else: "add_note"}
class="space-y-4"
>
<div class="form-control">
<textarea
name="admin_note[content]"
class="textarea textarea-bordered w-full h-24"
placeholder={gettext("Write a note about this user...")}
required
>{Phoenix.HTML.Form.input_value(@note_form, :content)}</textarea>
</div>
<div class="flex justify-end gap-2">
<%= if @editing_note_uuid do %>
<button type="button" class="btn btn-ghost" phx-click="cancel_edit">
{gettext("Cancel")}
</button>
<button type="submit" class="btn btn-primary">
<.icon name="hero-check" class="w-4 h-4" />
{gettext("Update Note")}
</button>
<% else %>
<button type="submit" class="btn btn-primary">
<.icon name="hero-plus" class="w-4 h-4" />
{gettext("Add Note")}
</button>
<% end %>
</div>
</.form>
</div>
</div>
<%!-- Notes List --%>
<%= if length(@admin_notes) > 0 do %>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg">
{gettext("Notes")}
<span class="badge badge-neutral">{length(@admin_notes)}</span>
</h3>
<div class="divide-y divide-base-300">
<%= for note <- @admin_notes do %>
<div class="py-4 first:pt-0 last:pb-0">
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-3 flex-1">
<.user_avatar user={note.author} size="sm" class="!w-8 !h-8 !text-sm" />
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 flex-wrap">
<span class="font-medium text-base-content">
<%= if note.author.first_name || note.author.last_name do %>
{note.author.first_name} {note.author.last_name}
<% else %>
{note.author.email}
<% end %>
</span>
<span class="text-sm text-base-content/50">
<.time_ago datetime={note.inserted_at} />
</span>
<%= if note.updated_at != note.inserted_at do %>
<span class="text-xs text-base-content/40">
({gettext("edited")})
</span>
<% end %>
</div>
<p class="mt-1 text-base-content whitespace-pre-wrap">
{note.content}
</p>
</div>
</div>
<div class="flex gap-1 shrink-0">
<button
type="button"
class="btn btn-ghost btn-xs tooltip tooltip-bottom"
phx-click="edit_note"
phx-value-uuid={note.uuid}
data-tip={gettext("Edit")}
>
<.icon name="hero-pencil" class="w-4 h-4 hidden sm:inline" />
<span class="sm:hidden whitespace-nowrap">{gettext("Edit")}</span>
</button>
<button
type="button"
class="btn btn-ghost btn-xs text-error tooltip tooltip-bottom"
phx-click="delete_note"
phx-value-uuid={note.uuid}
data-confirm={gettext("Are you sure you want to delete this note?")}
data-tip={gettext("Delete")}
>
<.icon name="hero-trash" class="w-4 h-4 hidden sm:inline" />
<span class="sm:hidden whitespace-nowrap">{gettext("Delete")}</span>
</button>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>
<% else %>
<div class="card bg-base-200 shadow-sm">
<div class="card-body items-center text-center py-12">
<.icon name="hero-document-text" class="w-12 h-12 text-base-content/30" />
<p class="text-base-content/50 mt-2">{gettext("No notes yet")}</p>
<p class="text-sm text-base-content/40">
{gettext("Add a note above to start documenting information about this user.")}
</p>
</div>
</div>
<% end %>
</div>
<% end %>
<%!-- Members Tab Content --%>
<%= if @active_tab == "members" && @org_accounts_enabled && @user.account_type == "organization" do %>
<div class="card bg-base-200 shadow-sm">
<div class="card-body">
<h3 class="card-title text-lg mb-4">{gettext("Organization Members")}</h3>
<%= if Enum.empty?(@organization_members) do %>
<div class="text-center py-8 text-base-content/70">
<.icon name="hero-user-group" class="w-12 h-12 mx-auto mb-2 opacity-50" />
<p>{gettext("No members yet")}</p>
<p class="text-sm mt-1">{gettext("Add members from the user edit page")}</p>
</div>
<% else %>
<div class="overflow-x-auto">
<table class="table table-zebra">
<thead>
<tr>
<th>{gettext("Name")}</th>
<th>{gettext("Email")}</th>
<th>{gettext("Status")}</th>
<th>{gettext("Actions")}</th>
</tr>
</thead>
<tbody>
<%= for member <- @organization_members do %>
<tr>
<td>
<div class="flex items-center gap-3">
<.user_avatar user={member} size="sm" />
<div>
<div class="font-medium">
{PhoenixKit.Users.Auth.User.full_name(member) || "-"}
</div>
<%= if member.username do %>
<div class="text-xs text-base-content/60">@{member.username}</div>
<% end %>
</div>
</div>
</td>
<td>{member.email}</td>
<td>
<%= if member.is_active do %>
<span class="badge badge-success badge-sm h-auto">
{gettext("Active")}
</span>
<% else %>
<span class="badge badge-error badge-sm h-auto">
{gettext("Inactive")}
</span>
<% end %>
</td>
<td>
<div class="flex gap-1">
<.pk_link
navigate={"/admin/users/view/#{member.uuid}"}
class="btn btn-ghost btn-xs"
title={gettext("View")}
>
<.icon name="hero-eye" class="w-4 h-4" />
</.pk_link>
<button
type="button"
class="btn btn-ghost btn-xs text-error"
phx-click="remove_member"
phx-value-uuid={member.uuid}
title={gettext("Remove from organization")}
data-confirm={
gettext("Are you sure you want to remove this member?")
}
>
<.icon name="hero-x-mark" class="w-4 h-4" />
</button>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
<%!-- Delete Confirmation Modal --%>
<%= if @show_delete_modal do %>
<div class="modal modal-open">
<div class="modal-box max-w-2xl">
<h3 class="font-bold text-lg text-error flex items-center gap-2">
<.icon name="hero-exclamation-triangle" class="w-6 h-6" />
{gettext("Delete User")}
</h3>
<div class="py-4 space-y-4">
<p class="text-base-content">
{gettext("Are you sure you want to permanently delete")}
<strong>{@user.email}</strong>? {gettext("This action cannot be undone.")}
</p>
<div class="bg-warning/10 border border-warning/30 rounded-lg p-4">
<h4 class="font-semibold text-warning mb-2">
{gettext("Data that will be permanently deleted:")}
</h4>
<ul class="list-disc list-inside text-sm text-base-content/80 space-y-1">
<li>{gettext("User account and profile information")}</li>
<li>{gettext("All session tokens and authentication data")}</li>
<li>{gettext("OAuth provider connections")}</li>
<li>{gettext("Billing profiles and payment methods")}</li>
<li>{gettext("Shopping carts and pending orders")}</li>
<li>{gettext("Admin notes about this user")}</li>
</ul>
</div>
<div class="bg-info/10 border border-info/30 rounded-lg p-4">
<h4 class="font-semibold text-info mb-2">
{gettext("Data that will be anonymized (preserved without PII):")}
</h4>
<ul class="list-disc list-inside text-sm text-base-content/80 space-y-1">
<li>{gettext("Orders - Financial records preserved, anonymized")}</li>
<li>{gettext("Posts - Content preserved, marked as deleted author")}</li>
<li>{gettext("Comments - Content preserved, marked as deleted author")}</li>
<li>{gettext("Support tickets - History preserved, anonymized")}</li>
<li>{gettext("Email logs - Compliance records retained, anonymized")}</li>
<li>{gettext("Files - Content preserved, ownership removed")}</li>
</ul>
</div>
</div>
<div class="modal-action">
<button type="button" class="btn btn-error" phx-click="delete_user">
<.icon name="hero-trash" class="w-4 h-4 mr-2" />
{gettext("Delete User")}
</button>
<button type="button" class="btn btn-outline" phx-click="hide_delete_modal">
{gettext("Cancel")}
</button>
</div>
</div>
<div class="modal-backdrop" phx-click="hide_delete_modal"></div>
</div>
<% end %>
<%!-- Role Management Modal --%>
<%= if @show_role_modal do %>
<div class="modal modal-open">
<div class="modal-box max-w-2xl">
<h3 class="font-bold text-xl mb-4">
Manage Roles for {@user.email}
</h3>
<form phx-submit="sync_user_roles" class="space-y-4">
<div class="space-y-2">
<% user_role_names = Enum.map(@user.roles || [], & &1.name) %>
<%= for role <- @all_roles do %>
<% owner_role? = role.name == PhoenixKit.Users.Role.system_roles().owner %>
<div class="form-control">
<label
class={[
"label justify-start gap-3",
owner_role? && "cursor-not-allowed opacity-70"
]}
title={if owner_role?, do: gettext("Owner role cannot be modified"), else: nil}
>
<input
type="checkbox"
name={"roles[#{role.name}]"}
value={role.name}
class="checkbox"
checked={role.name in user_role_names}
disabled={owner_role?}
/>
<div class="flex flex-wrap items-center gap-2">
<.role_badge role={role} size={:sm} />
<%= if role.is_system_role do %>
<span class="badge badge-outline badge-xs h-auto">
{gettext("System")}
</span>
<% end %>
<%= if role.description do %>
<span class="text-sm text-base-content/70">- {role.description}</span>
<% end %>
</div>
</label>
</div>
<% end %>
</div>
<%= if Enum.empty?(@all_roles) do %>
<div class="text-center py-8 text-base-content/70">
<p>{gettext("No roles available. Create roles first in Role Management.")}</p>
</div>
<% end %>
<div class="modal-action">
<button type="submit" class="btn btn-primary">
{gettext("Update Roles")}
</button>
<button type="button" phx-click="hide_role_management" class="btn btn-outline">
{gettext("Cancel")}
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="hide_role_management"></div>
</div>
<% end %>
<%!-- Status / Confirmation Change Modal --%>
<%= if assigns[:confirmation_modal] && @confirmation_modal.show do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">{@confirmation_modal.title}</h3>
<p class="py-4">{@confirmation_modal.message}</p>
<div class="modal-action">
<button class="btn btn-ghost" phx-click="cancel_confirmation">
{gettext("Cancel")}
</button>
<button
class="btn btn-primary"
phx-click="confirm_action"
phx-value-action={@confirmation_modal.action}
>
{@confirmation_modal.button_text}
</button>
</div>
</div>
</div>
<% end %>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>