Packages

phoenix_kit

1.7.113
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
phoenix_kit lib phoenix_kit_web live users live_sessions.html.heex
Raw

lib/phoenix_kit_web/live/users/live_sessions.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="Live Sessions - {@project_title} Admin"
current_path={@url_path}
project_title={@project_title}
>
<div class="container mx-auto px-4 py-8">
<.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">Live Sessions</h1>
<p class="text-sm text-base-content/60 mt-0.5">
Real-time monitoring of all active sessions with live updates.
Last updated: {Calendar.strftime(@last_updated, "%H:%M:%S")}
</p>
<:actions>
<%!-- Live Indicator --%>
<div class="flex items-center gap-2 px-3 py-1 bg-success/10 text-success rounded-full">
<div class="w-2 h-2 bg-success rounded-full animate-pulse"></div>
<span class="text-xs font-medium">LIVE</span>
</div>
<%!-- Auto-refresh toggle --%>
<button
phx-click="toggle_auto_refresh"
class={[
"btn btn-sm",
if(@auto_refresh, do: "btn-success", else: "btn-outline")
]}
>
<%= if @auto_refresh do %>
<.icon name="hero-pause-circle" class="w-5 h-5" /> Auto-refresh ON
<% else %>
<.icon name="hero-play-circle" class="w-5 h-5" /> Auto-refresh OFF
<% end %>
</button>
<%!-- Manual refresh --%>
<button
phx-click="refresh_now"
class="btn btn-sm btn-outline"
>
<.icon name="hero-arrow-path" class="w-5 h-5" /> Refresh
</button>
</:actions>
</.admin_page_header>
<%!-- Statistics Cards --%>
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<%!-- Total Sessions --%>
<PhoenixKitWeb.Components.Core.StatCard.stat_card
compact={true}
rounded="xl"
value={@presence_stats.total_sessions}
title="Total Sessions"
subtitle="Real-time connections"
>
<:icon>
<.icon name="hero-user-circle" class="w-5 h-5" />
</:icon>
</PhoenixKitWeb.Components.Core.StatCard.stat_card>
<%!-- Anonymous Sessions --%>
<PhoenixKitWeb.Components.Core.StatCard.stat_card
compact={true}
rounded="xl"
value={@presence_stats.anonymous_sessions}
title="Anonymous"
subtitle="Unregistered visitors"
>
<:icon>
<.icon name="hero-user" class="w-5 h-5" />
</:icon>
</PhoenixKitWeb.Components.Core.StatCard.stat_card>
<%!-- Authenticated Sessions --%>
<PhoenixKitWeb.Components.Core.StatCard.stat_card
compact={true}
rounded="xl"
value={@presence_stats.authenticated_sessions}
title="Authenticated"
subtitle="Logged in users"
>
<:icon>
<.icon name="hero-check-circle" class="w-5 h-5" />
</:icon>
</PhoenixKitWeb.Components.Core.StatCard.stat_card>
<%!-- Unique Visitors --%>
<PhoenixKitWeb.Components.Core.StatCard.stat_card
compact={true}
rounded="xl"
value={@presence_stats.unique_anonymous_visitors}
title="Unique Visitors"
subtitle="Individual connections"
>
<:icon>
<.icon name="hero-user-group" class="w-5 h-5" />
</:icon>
</PhoenixKitWeb.Components.Core.StatCard.stat_card>
</div>
<%!-- Sessions Table / Cards --%>
<.table_default
id="live-sessions-table"
variant="zebra"
toggleable={true}
items={@sessions}
card_fields={
fn session ->
[
%{label: "IP Address", value: session.ip_address || "Unknown"},
%{label: "Current Page", value: session.current_page || "—"},
%{
label: "Connected",
value: Calendar.strftime(session.connected_at, "%H:%M:%S %d/%m/%Y")
},
%{label: "Status", value: "● Active"}
]
end
}
>
<:toolbar_title>
<div class="flex flex-wrap items-center gap-2">
<%!-- Search --%>
<form phx-change="search" class="contents">
<label class="input input-sm w-40 sm:w-52 lg:w-64 min-w-0">
<.icon name="hero-magnifying-glass" class="h-4 w-4 opacity-50" />
<input
type="search"
name="search"
value={@search_query}
placeholder="Search by IP, User-Agent, email..."
class="grow"
phx-debounce="300"
/>
</label>
</form>
<%!-- Filter Tabs --%>
<div class="tabs tabs-boxed tabs-sm">
<button
phx-click="filter_by"
phx-value-type="all"
class={["tab", if(@filter_type == "all", do: "tab-active", else: "")]}
>
All ({@total_sessions})
</button>
<button
phx-click="filter_by"
phx-value-type="anonymous"
class={["tab", if(@filter_type == "anonymous", do: "tab-active", else: "")]}
>
Anonymous ({@presence_stats.anonymous_sessions})
</button>
<button
phx-click="filter_by"
phx-value-type="authenticated"
class={["tab", if(@filter_type == "authenticated", do: "tab-active", else: "")]}
>
Authenticated ({@presence_stats.authenticated_sessions})
</button>
</div>
</div>
</:toolbar_title>
<:card_header :let={session}>
<div class="flex items-start justify-between gap-2">
<%= if session.type == :authenticated do %>
<% user = Map.get(@users_map, session.user_uuid) %>
<div class="flex items-center gap-2 min-w-0">
<.user_avatar user={user || %{email: session.user_email}} size="sm" />
<div class="min-w-0">
<div class="font-medium text-sm truncate">{session.user_email}</div>
<div class="text-xs text-base-content/70 font-mono truncate">
{session.user_uuid}
</div>
</div>
</div>
<div class="badge badge-success badge-sm h-auto shrink-0">
<.icon name="hero-shield-check" class="w-3 h-3 mr-1" /> Auth
</div>
<% else %>
<div class="flex items-center gap-2 min-w-0">
<div class="w-8 h-8 bg-warning rounded-full flex shrink-0 items-center justify-center text-warning-content text-xs font-bold">
?
</div>
<div class="min-w-0">
<div class="font-medium text-sm">Anonymous User</div>
<div class="text-xs text-base-content/70 font-mono truncate">
{session.session_id}
</div>
</div>
</div>
<div class="badge badge-warning badge-sm h-auto shrink-0">
<.icon name="hero-user" class="w-3 h-3 mr-1" /> Anon
</div>
<% end %>
</div>
</:card_header>
<.table_default_header>
<.table_default_row>
<.sort_header_cell field={:type} sort={@sort}>Type</.sort_header_cell>
<.table_default_header_cell>User/Session</.table_default_header_cell>
<.table_default_header_cell>IP Address</.table_default_header_cell>
<.table_default_header_cell>Current Page</.table_default_header_cell>
<.sort_header_cell field={:connected_at} sort={@sort}>Connected</.sort_header_cell>
<.table_default_header_cell>Status</.table_default_header_cell>
</.table_default_row>
</.table_default_header>
<.table_default_body>
<%= if Enum.empty?(@sessions) do %>
<.table_default_row>
<.table_default_cell class="text-center py-12" colspan={6}>
<div class="text-base-content/50 mb-4">
<.icon name="hero-inbox" class="w-16 h-16 mx-auto" />
</div>
<h3 class="text-lg font-medium text-base-content mb-2">No active sessions</h3>
<p class="text-base-content/70">
There are currently no active sessions to display.
</p>
</.table_default_cell>
</.table_default_row>
<% else %>
<%= for session <- @sessions do %>
<.table_default_row>
<.table_default_cell>
<div class="flex items-center gap-2">
<%= if session.type == :authenticated do %>
<div class="badge badge-success badge-sm h-auto">
<.icon name="hero-shield-check" class="w-3 h-3 mr-1" /> Auth
</div>
<% else %>
<div class="badge badge-warning badge-sm h-auto">
<.icon name="hero-user" class="w-3 h-3 mr-1" /> Anon
</div>
<% end %>
</div>
</.table_default_cell>
<.table_default_cell>
<%= if session.type == :authenticated do %>
<% user = Map.get(@users_map, session.user_uuid) %>
<div class="flex items-center gap-2">
<.user_avatar user={user || %{email: session.user_email}} size="sm" />
<div>
<div class="font-medium text-sm">{session.user_email}</div>
<div class="text-xs text-base-content/70">ID: {session.user_uuid}</div>
</div>
</div>
<% else %>
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-warning rounded-full flex items-center justify-center text-warning-content text-xs font-bold">
?
</div>
<div>
<div class="font-medium text-sm">Anonymous User</div>
<div class="text-xs text-base-content/70 font-mono">
{session.session_id}
</div>
</div>
</div>
<% end %>
</.table_default_cell>
<.table_default_cell>
<code class="text-xs bg-base-200 px-2 py-1 rounded">
{session.ip_address || "Unknown"}
</code>
</.table_default_cell>
<.table_default_cell>
<%= if session.current_page do %>
<code class="text-xs bg-base-200 px-2 py-1 rounded">
{session.current_page}
</code>
<% else %>
<span class="text-base-content/50"></span>
<% end %>
</.table_default_cell>
<.table_default_cell>
<.time_ago datetime={session.connected_at} />
</.table_default_cell>
<.table_default_cell>
<div class="flex items-center gap-1">
<div class="w-2 h-2 bg-success rounded-full animate-pulse"></div>
<span class="text-xs text-success">Active</span>
</div>
</.table_default_cell>
</.table_default_row>
<% end %>
<% end %>
</.table_default_body>
</.table_default>
<%!-- Pagination --%>
<%= if @total_pages > 1 do %>
<div class="flex justify-center mt-6">
<div class="btn-group">
<%= for page_num <- 1..@total_pages do %>
<button
phx-click="change_page"
phx-value-page={page_num}
class={[
"btn btn-sm",
if(page_num == @page, do: "btn-active", else: "")
]}
>
{page_num}
</button>
<% end %>
</div>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>