Packages
phoenix_kit
1.7.21
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/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">
<%!-- Header with Live Indicator --%>
<div class="mb-8">
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<h1 class="text-3xl font-bold text-base-content">
Live Sessions
</h1>
<%!-- 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>
</div>
<%!-- Controls --%>
<div class="flex items-center gap-2">
<%!-- 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>
</div>
</div>
<p class="text-base-content/70 mt-2">
Real-time monitoring of all active sessions with live updates.
Last updated: {Calendar.strftime(@last_updated, "%H:%M:%S")}
</p>
</div>
<%!-- 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>
<%!-- Filters and Search --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body p-4">
<div class="flex flex-col md:flex-row gap-4 items-center justify-between">
<%!-- Search --%>
<div class="flex-1 max-w-md">
<form phx-submit="search" phx-change="search" class="form-control">
<div class="input-group">
<input
type="text"
name="search"
value={@search_query}
placeholder="Search by IP, User-Agent, email..."
class="input input-bordered flex-1"
/>
<button type="submit" class="btn btn-square">
<.icon name="hero-magnifying-glass" class="w-5 h-5" />
</button>
</div>
</form>
</div>
<%!-- Filter Tabs --%>
<div class="tabs tabs-boxed">
<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>
</div>
</div>
<%!-- Sessions Table --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body p-0">
<.table_default variant="zebra">
<.table_default_header>
<.table_default_row>
<.table_default_header_cell>
<button
phx-click="sort_by"
phx-value-field="type"
class="flex items-center gap-1 cursor-pointer"
>
Type
<%= if @sort_by == :type do %>
<%= if @sort_order == :asc do %>
<.icon name="hero-chevron-up" class="w-4 h-4" />
<% else %>
<.icon name="hero-chevron-down" class="w-4 h-4" />
<% end %>
<% end %>
</button>
</.table_default_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>
<.table_default_header_cell>
<button
phx-click="sort_by"
phx-value-field="connected_at"
class="flex items-center gap-1 cursor-pointer"
>
Connected
<%= if @sort_by == :connected_at do %>
<%= if @sort_order == :asc do %>
<.icon name="hero-chevron-up" class="w-4 h-4" />
<% else %>
<.icon name="hero-chevron-down" class="w-4 h-4" />
<% end %>
<% end %>
</button>
</.table_default_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">
<.icon name="hero-shield-check" class="w-3 h-3 mr-1" /> Auth
</div>
<% else %>
<div class="badge badge-warning badge-sm">
<.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_id) %>
<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_id}
</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>
</div>
</div>
<%!-- 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="goto_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>