paginate login history and improve user settings
This commit is contained in:
parent
feed25080c
commit
b08daf9a88
1 changed files with 166 additions and 59 deletions
|
|
@ -19,7 +19,6 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, "Account Settings")
|
|> assign(:page_title, "Account Settings")
|
||||||
|> assign(:active_tab, "personal")
|
|
||||||
|> assign(:current_token, current_token)
|
|> assign(:current_token, current_token)
|
||||||
|> assign_changesets()
|
|> assign_changesets()
|
||||||
|> assign_profile_form()
|
|> assign_profile_form()
|
||||||
|
|
@ -50,8 +49,20 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("switch_tab", %{"tab" => tab}, socket) do
|
def handle_params(params, _url, socket) do
|
||||||
{:noreply, assign(socket, :active_tab, tab)}
|
# Default to "personal" tab if no tab param is provided
|
||||||
|
tab = Map.get(params, "tab", "personal")
|
||||||
|
|
||||||
|
# Get page from params, default to 1
|
||||||
|
page = params |> Map.get("page", "1") |> String.to_integer()
|
||||||
|
|
||||||
|
socket =
|
||||||
|
socket
|
||||||
|
|> assign(:active_tab, tab)
|
||||||
|
|> assign(:login_history_page, page)
|
||||||
|
|> assign_login_history()
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
@ -302,18 +313,6 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("load_more_history", _params, socket) do
|
|
||||||
next_page = socket.assigns.login_history_page + 1
|
|
||||||
|
|
||||||
socket =
|
|
||||||
socket
|
|
||||||
|> assign(:login_history_page, next_page)
|
|
||||||
|> assign_login_history()
|
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Security tab - TOTP Device Management
|
# Security tab - TOTP Device Management
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
@ -497,15 +496,24 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
defp assign_login_history(socket) do
|
defp assign_login_history(socket) do
|
||||||
user = socket.assigns.current_scope.user
|
user = socket.assigns.current_scope.user
|
||||||
page = socket.assigns[:login_history_page] || 1
|
page = socket.assigns[:login_history_page] || 1
|
||||||
limit = 20
|
per_page = 5
|
||||||
offset = (page - 1) * limit
|
offset = (page - 1) * per_page
|
||||||
|
|
||||||
history = Accounts.list_user_login_history(user.id, limit: limit, offset: offset)
|
# Get total count and history for current page
|
||||||
has_more = length(history) == limit
|
total_count = Accounts.count_user_login_attempts(user.id)
|
||||||
|
history = Accounts.list_user_login_history(user.id, limit: per_page, offset: offset)
|
||||||
|
|
||||||
|
total_pages = ceil(total_count / per_page)
|
||||||
|
page = max(1, min(page, max(1, total_pages)))
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> assign(:login_history, history)
|
|> assign(:login_history, history)
|
||||||
|> assign(:has_more_history, has_more)
|
|> assign(:login_history_pagination, %{
|
||||||
|
page: page,
|
||||||
|
per_page: per_page,
|
||||||
|
total_count: total_count,
|
||||||
|
total_pages: total_pages
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_security_alerts(socket) do
|
defp assign_security_alerts(socket) do
|
||||||
|
|
@ -599,6 +607,28 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generate pagination range with ellipsis for large page counts
|
||||||
|
# Shows: [1] ... [4] [5] [6] ... [20] when on page 5 of 20
|
||||||
|
defp pagination_range(current_page, total_pages) do
|
||||||
|
cond do
|
||||||
|
total_pages <= 7 ->
|
||||||
|
# Show all pages if 7 or fewer
|
||||||
|
Enum.to_list(1..total_pages)
|
||||||
|
|
||||||
|
current_page <= 4 ->
|
||||||
|
# Near the start
|
||||||
|
[1, 2, 3, 4, 5, :ellipsis, total_pages]
|
||||||
|
|
||||||
|
current_page >= total_pages - 3 ->
|
||||||
|
# Near the end
|
||||||
|
[1, :ellipsis, total_pages - 4, total_pages - 3, total_pages - 2, total_pages - 1, total_pages]
|
||||||
|
|
||||||
|
true ->
|
||||||
|
# In the middle
|
||||||
|
[1, :ellipsis, current_page - 1, current_page, current_page + 1, :ellipsis, total_pages]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
|
|
@ -622,10 +652,8 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
class="flex min-w-full flex-none gap-x-6 px-4 text-sm/6 font-semibold text-gray-500 sm:px-6 lg:px-8 dark:text-gray-400"
|
class="flex min-w-full flex-none gap-x-6 px-4 text-sm/6 font-semibold text-gray-500 sm:px-6 lg:px-8 dark:text-gray-400"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<.link
|
||||||
href="#"
|
patch={~p"/users/settings?tab=personal"}
|
||||||
phx-click="switch_tab"
|
|
||||||
phx-value-tab="personal"
|
|
||||||
class={
|
class={
|
||||||
if @active_tab == "personal",
|
if @active_tab == "personal",
|
||||||
do: "text-indigo-600 dark:text-indigo-400",
|
do: "text-indigo-600 dark:text-indigo-400",
|
||||||
|
|
@ -633,13 +661,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Personal
|
Personal
|
||||||
</a>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<.link
|
||||||
href="#"
|
patch={~p"/users/settings?tab=account"}
|
||||||
phx-click="switch_tab"
|
|
||||||
phx-value-tab="account"
|
|
||||||
class={
|
class={
|
||||||
if @active_tab == "account",
|
if @active_tab == "account",
|
||||||
do: "text-indigo-600 dark:text-indigo-400",
|
do: "text-indigo-600 dark:text-indigo-400",
|
||||||
|
|
@ -647,13 +673,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Account
|
Account
|
||||||
</a>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<.link
|
||||||
href="#"
|
patch={~p"/users/settings?tab=sessions"}
|
||||||
phx-click="switch_tab"
|
|
||||||
phx-value-tab="sessions"
|
|
||||||
class={
|
class={
|
||||||
if @active_tab == "sessions",
|
if @active_tab == "sessions",
|
||||||
do: "text-indigo-600 dark:text-indigo-400",
|
do: "text-indigo-600 dark:text-indigo-400",
|
||||||
|
|
@ -661,13 +685,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Sessions
|
Sessions
|
||||||
</a>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<.link
|
||||||
href="#"
|
patch={~p"/users/settings?tab=api"}
|
||||||
phx-click="switch_tab"
|
|
||||||
phx-value-tab="api"
|
|
||||||
class={
|
class={
|
||||||
if @active_tab == "api",
|
if @active_tab == "api",
|
||||||
do: "text-indigo-600 dark:text-indigo-400",
|
do: "text-indigo-600 dark:text-indigo-400",
|
||||||
|
|
@ -675,13 +697,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
API
|
API
|
||||||
</a>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<.link
|
||||||
href="#"
|
patch={~p"/users/settings?tab=notifications"}
|
||||||
phx-click="switch_tab"
|
|
||||||
phx-value-tab="notifications"
|
|
||||||
class={
|
class={
|
||||||
if @active_tab == "notifications",
|
if @active_tab == "notifications",
|
||||||
do: "text-indigo-600 dark:text-indigo-400",
|
do: "text-indigo-600 dark:text-indigo-400",
|
||||||
|
|
@ -689,13 +709,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Notifications
|
Notifications
|
||||||
</a>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<.link
|
||||||
href="#"
|
patch={~p"/users/settings?tab=security"}
|
||||||
phx-click="switch_tab"
|
|
||||||
phx-value-tab="security"
|
|
||||||
class={
|
class={
|
||||||
if @active_tab == "security",
|
if @active_tab == "security",
|
||||||
do: "text-indigo-600 dark:text-indigo-400",
|
do: "text-indigo-600 dark:text-indigo-400",
|
||||||
|
|
@ -703,7 +721,7 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Security
|
Security
|
||||||
</a>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
|
|
@ -1389,15 +1407,104 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= if @has_more_history do %>
|
<%= if @login_history_pagination.total_pages > 1 do %>
|
||||||
<div class="mt-6 flex">
|
<div class="mt-6 flex items-center justify-between border-t border-gray-200 dark:border-white/10 pt-6">
|
||||||
<button
|
<!-- Mobile: Previous/Next only -->
|
||||||
type="button"
|
<div class="flex flex-1 justify-between sm:hidden">
|
||||||
phx-click="load_more_history"
|
<.link
|
||||||
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs inset-ring-1 inset-ring-gray-300 hover:bg-gray-100 dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/10 dark:hover:bg-white/20"
|
:if={@login_history_pagination.page > 1}
|
||||||
>
|
patch={
|
||||||
Load More
|
~p"/users/settings?tab=#{@active_tab}&page=#{@login_history_pagination.page - 1}"
|
||||||
</button>
|
}
|
||||||
|
class="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-white/10 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</.link>
|
||||||
|
<.link
|
||||||
|
:if={@login_history_pagination.page < @login_history_pagination.total_pages}
|
||||||
|
patch={
|
||||||
|
~p"/users/settings?tab=#{@active_tab}&page=#{@login_history_pagination.page + 1}"
|
||||||
|
}
|
||||||
|
class="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-white/10 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</.link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop: Full pagination with page numbers -->
|
||||||
|
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-700 dark:text-gray-400">
|
||||||
|
Showing
|
||||||
|
<span class="font-medium">
|
||||||
|
{(@login_history_pagination.page - 1) * @login_history_pagination.per_page +
|
||||||
|
1}
|
||||||
|
</span>
|
||||||
|
to
|
||||||
|
<span class="font-medium">
|
||||||
|
{min(
|
||||||
|
@login_history_pagination.page * @login_history_pagination.per_page,
|
||||||
|
@login_history_pagination.total_count
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
of <span class="font-medium">{@login_history_pagination.total_count}</span>
|
||||||
|
results
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<nav
|
||||||
|
class="isolate inline-flex -space-x-px rounded-md shadow-sm"
|
||||||
|
aria-label="Pagination"
|
||||||
|
>
|
||||||
|
<!-- Previous button -->
|
||||||
|
<.link
|
||||||
|
:if={@login_history_pagination.page > 1}
|
||||||
|
patch={
|
||||||
|
~p"/users/settings?tab=#{@active_tab}&page=#{@login_history_pagination.page - 1}"
|
||||||
|
}
|
||||||
|
class="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:ring-white/10 dark:hover:bg-gray-800"
|
||||||
|
>
|
||||||
|
<span class="sr-only">Previous</span>
|
||||||
|
<.icon name="hero-chevron-left" class="h-5 w-5" />
|
||||||
|
</.link>
|
||||||
|
<!-- Page numbers with ellipsis -->
|
||||||
|
<%= for page_num <- pagination_range(@login_history_pagination.page, @login_history_pagination.total_pages) do %>
|
||||||
|
<%= if page_num == :ellipsis do %>
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-700 ring-1 ring-inset ring-gray-300 dark:text-gray-400 dark:ring-white/10">
|
||||||
|
...
|
||||||
|
</span>
|
||||||
|
<% else %>
|
||||||
|
<.link
|
||||||
|
patch={~p"/users/settings?tab=#{@active_tab}&page=#{page_num}"}
|
||||||
|
class={[
|
||||||
|
"relative inline-flex items-center px-4 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 dark:ring-white/10",
|
||||||
|
if @login_history_pagination.page == page_num do
|
||||||
|
"z-10 bg-blue-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||||
|
else
|
||||||
|
"text-gray-900 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||||
|
end
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{page_num}
|
||||||
|
</.link>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<!-- Next button -->
|
||||||
|
<.link
|
||||||
|
:if={
|
||||||
|
@login_history_pagination.page < @login_history_pagination.total_pages
|
||||||
|
}
|
||||||
|
patch={
|
||||||
|
~p"/users/settings?tab=#{@active_tab}&page=#{@login_history_pagination.page + 1}"
|
||||||
|
}
|
||||||
|
class="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:ring-white/10 dark:hover:bg-gray-800"
|
||||||
|
>
|
||||||
|
<span class="sr-only">Next</span>
|
||||||
|
<.icon name="hero-chevron-right" class="h-5 w-5" />
|
||||||
|
</.link>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue