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
|
||||
|> assign(:page_title, "Account Settings")
|
||||
|> assign(:active_tab, "personal")
|
||||
|> assign(:current_token, current_token)
|
||||
|> assign_changesets()
|
||||
|> assign_profile_form()
|
||||
|
|
@ -50,8 +49,20 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("switch_tab", %{"tab" => tab}, socket) do
|
||||
{:noreply, assign(socket, :active_tab, tab)}
|
||||
def handle_params(params, _url, socket) do
|
||||
# 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
|
||||
|
||||
@impl true
|
||||
|
|
@ -302,18 +313,6 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
{:noreply, socket}
|
||||
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
|
||||
|
||||
@impl true
|
||||
|
|
@ -497,15 +496,24 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
defp assign_login_history(socket) do
|
||||
user = socket.assigns.current_scope.user
|
||||
page = socket.assigns[:login_history_page] || 1
|
||||
limit = 20
|
||||
offset = (page - 1) * limit
|
||||
per_page = 5
|
||||
offset = (page - 1) * per_page
|
||||
|
||||
history = Accounts.list_user_login_history(user.id, limit: limit, offset: offset)
|
||||
has_more = length(history) == limit
|
||||
# Get total count and history for current page
|
||||
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
|
||||
|> 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
|
||||
|
||||
defp assign_security_alerts(socket) do
|
||||
|
|
@ -599,6 +607,28 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
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
|
||||
def render(assigns) do
|
||||
~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"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
phx-click="switch_tab"
|
||||
phx-value-tab="personal"
|
||||
<.link
|
||||
patch={~p"/users/settings?tab=personal"}
|
||||
class={
|
||||
if @active_tab == "personal",
|
||||
do: "text-indigo-600 dark:text-indigo-400",
|
||||
|
|
@ -633,13 +661,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
}
|
||||
>
|
||||
Personal
|
||||
</a>
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
phx-click="switch_tab"
|
||||
phx-value-tab="account"
|
||||
<.link
|
||||
patch={~p"/users/settings?tab=account"}
|
||||
class={
|
||||
if @active_tab == "account",
|
||||
do: "text-indigo-600 dark:text-indigo-400",
|
||||
|
|
@ -647,13 +673,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
}
|
||||
>
|
||||
Account
|
||||
</a>
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
phx-click="switch_tab"
|
||||
phx-value-tab="sessions"
|
||||
<.link
|
||||
patch={~p"/users/settings?tab=sessions"}
|
||||
class={
|
||||
if @active_tab == "sessions",
|
||||
do: "text-indigo-600 dark:text-indigo-400",
|
||||
|
|
@ -661,13 +685,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
}
|
||||
>
|
||||
Sessions
|
||||
</a>
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
phx-click="switch_tab"
|
||||
phx-value-tab="api"
|
||||
<.link
|
||||
patch={~p"/users/settings?tab=api"}
|
||||
class={
|
||||
if @active_tab == "api",
|
||||
do: "text-indigo-600 dark:text-indigo-400",
|
||||
|
|
@ -675,13 +697,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
}
|
||||
>
|
||||
API
|
||||
</a>
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
phx-click="switch_tab"
|
||||
phx-value-tab="notifications"
|
||||
<.link
|
||||
patch={~p"/users/settings?tab=notifications"}
|
||||
class={
|
||||
if @active_tab == "notifications",
|
||||
do: "text-indigo-600 dark:text-indigo-400",
|
||||
|
|
@ -689,13 +709,11 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
}
|
||||
>
|
||||
Notifications
|
||||
</a>
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
phx-click="switch_tab"
|
||||
phx-value-tab="security"
|
||||
<.link
|
||||
patch={~p"/users/settings?tab=security"}
|
||||
class={
|
||||
if @active_tab == "security",
|
||||
do: "text-indigo-600 dark:text-indigo-400",
|
||||
|
|
@ -703,7 +721,7 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
}
|
||||
>
|
||||
Security
|
||||
</a>
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link
|
||||
|
|
@ -1389,15 +1407,104 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<%= if @has_more_history do %>
|
||||
<div class="mt-6 flex">
|
||||
<button
|
||||
type="button"
|
||||
phx-click="load_more_history"
|
||||
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"
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
<%= if @login_history_pagination.total_pages > 1 do %>
|
||||
<div class="mt-6 flex items-center justify-between border-t border-gray-200 dark:border-white/10 pt-6">
|
||||
<!-- Mobile: Previous/Next only -->
|
||||
<div class="flex flex-1 justify-between sm:hidden">
|
||||
<.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-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>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue