diff --git a/lib/pinchflat_web/components/custom_components/table_components.ex b/lib/pinchflat_web/components/custom_components/table_components.ex index 1fad86e..cde3f9c 100644 --- a/lib/pinchflat_web/components/custom_components/table_components.ex +++ b/lib/pinchflat_web/components/custom_components/table_components.ex @@ -85,7 +85,7 @@ defmodule PinchflatWeb.CustomComponents.TableComponents do class={[ "flex h-8 w-8 items-center justify-center rounded", @page_number != 1 && "cursor-pointer hover:bg-primary hover:text-white", - @page_number == 1 && "cursor-not-allowed" + @page_number <= 1 && "cursor-not-allowed" ]} phx-click={@page_number != 1 && "page_change"} phx-value-direction="dec" @@ -103,7 +103,7 @@ defmodule PinchflatWeb.CustomComponents.TableComponents do class={[ "flex h-8 w-8 items-center justify-center rounded", @page_number != @total_pages && "cursor-pointer hover:bg-primary hover:text-white", - @page_number == @total_pages && "cursor-not-allowed" + @page_number >= @total_pages && "cursor-not-allowed" ]} phx-click={@page_number != @total_pages && "page_change"} phx-value-direction="inc" diff --git a/lib/pinchflat_web/controllers/sources/source_html/index.html.heex b/lib/pinchflat_web/controllers/sources/source_html/index.html.heex index 7673b46..4761552 100644 --- a/lib/pinchflat_web/controllers/sources/source_html/index.html.heex +++ b/lib/pinchflat_web/controllers/sources/source_html/index.html.heex @@ -11,13 +11,12 @@
-
- {live_render(@conn, PinchflatWeb.Sources.SourceLive.IndexTableLive, - session: %{ - "initial_sort_key" => :custom_name, - "initial_sort_direction" => :asc - } - )} -
+ {live_render(@conn, PinchflatWeb.Sources.SourceLive.IndexTableLive, + session: %{ + "initial_sort_key" => :custom_name, + "initial_sort_direction" => :asc, + "results_per_page" => 10 + } + )}
diff --git a/lib/pinchflat_web/controllers/sources/source_live/index_table_live.ex b/lib/pinchflat_web/controllers/sources/source_live/index_table_live.ex index a4068b3..aefa499 100644 --- a/lib/pinchflat_web/controllers/sources/source_live/index_table_live.ex +++ b/lib/pinchflat_web/controllers/sources/source_live/index_table_live.ex @@ -4,6 +4,7 @@ defmodule PinchflatWeb.Sources.SourceLive.IndexTableLive do use Pinchflat.Sources.SourcesQuery import PinchflatWeb.Helpers.SortingHelpers + import PinchflatWeb.Helpers.PaginationHelpers alias Pinchflat.Repo alias Pinchflat.Sources @@ -11,10 +12,15 @@ defmodule PinchflatWeb.Sources.SourceLive.IndexTableLive do alias Pinchflat.Media.MediaItem def mount(_params, session, socket) do - initial_params = %{ - sort_key: session["initial_sort_key"], - sort_direction: session["initial_sort_direction"] - } + limit = session["results_per_page"] + initial_params = + Map.merge( + %{ + sort_key: session["initial_sort_key"], + sort_direction: session["initial_sort_direction"] + }, + get_pagination_attributes(sources_query(), 1, limit) + ) socket |> assign(initial_params) @@ -22,6 +28,15 @@ defmodule PinchflatWeb.Sources.SourceLive.IndexTableLive do |> then(&{:ok, &1}) end + def handle_event("page_change", %{"direction" => direction}, %{assigns: assigns} = socket) do + new_page = update_page_number(assigns.page, direction, assigns.total_pages) + + socket + |> assign(get_pagination_attributes(sources_query(), new_page, assigns.limit)) + |> set_sources() + |> then(&{:noreply, &1}) + end + def handle_event("sort_update", %{"sort_key" => sort_key}, %{assigns: assigns} = socket) do new_sort_key = String.to_existing_atom(sort_key) @@ -36,15 +51,6 @@ defmodule PinchflatWeb.Sources.SourceLive.IndexTableLive do |> then(&{:noreply, &1}) end - defp set_sources(%{assigns: assigns} = socket) do - sources = - sources_query() - |> order_by(^[{assigns.sort_direction, sort_attr(assigns.sort_key)}, asc: :id]) - |> Repo.all() - - assign(socket, %{sources: sources}) - end - defp sort_attr(:pending_count), do: dynamic([s, mp, dl, pe], field(pe, :pending_count)) defp sort_attr(:downloaded_count), do: dynamic([s, mp, dl], field(dl, :downloaded_count)) defp sort_attr(:media_profile_name), do: dynamic([s, mp], field(mp, :name)) @@ -53,6 +59,17 @@ defmodule PinchflatWeb.Sources.SourceLive.IndexTableLive do defp sort_attr(:retention_period_days), do: dynamic([s], field(s, :retention_period_days)) defp sort_attr(_), do: sort_attr(:custom_name) + defp set_sources(%{assigns: assigns} = socket) do + sources = + sources_query() + |> order_by(^[{assigns.sort_direction, sort_attr(assigns.sort_key)}, asc: :id]) + |> limit(^assigns.limit) + |> offset(^assigns.offset) + |> Repo.all() + + assign(socket, %{sources: sources}) + end + defp sources_query do downloaded_subquery = from( diff --git a/lib/pinchflat_web/controllers/sources/source_live/index_table_live.html.heex b/lib/pinchflat_web/controllers/sources/source_live/index_table_live.html.heex index 7fb7a97..4d87655 100644 --- a/lib/pinchflat_web/controllers/sources/source_live/index_table_live.html.heex +++ b/lib/pinchflat_web/controllers/sources/source_live/index_table_live.html.heex @@ -1,40 +1,46 @@ -<.table rows={@sources} table_class="text-white" sort_key={@sort_key} sort_direction={@sort_direction}> - <:col :let={source} label="Name" sort_key="custom_name"> - <.subtle_link href={~p"/sources/#{source.id}"}> - {source.custom_name} - - - <:col :let={source} label="Pending" sort_key="pending_count"> - <.subtle_link href={~p"/sources/#{source.id}/#tab-pending"}> - <.localized_number number={source.pending_count} /> - - - <:col :let={source} label="Downloaded" sort_key="downloaded_count"> - <.subtle_link href={~p"/sources/#{source.id}/#tab-downloaded"}> - <.localized_number number={source.downloaded_count} /> - - - <:col :let={source} label="Retention" sort_key="retention_period_days"> - <%= if source.retention_period_days && source.retention_period_days > 0 do %> - <.localized_number number={source.retention_period_days} /> - <.pluralize count={source.retention_period_days} word="day" /> - <% else %> - - <% end %> - - <:col :let={source} label="Media Profile" sort_key="media_profile_name"> - <.subtle_link href={~p"/media_profiles/#{source.media_profile_id}"}> - {source.media_profile.name} - - - <:col :let={source} label="Enabled?" sort_key="enabled"> - <.live_component - module={PinchflatWeb.Sources.SourceLive.SourceEnableToggle} - source={source} - id={"source_#{source.id}_enabled"} - /> - - <:col :let={source} label="" class="flex place-content-evenly"> - <.icon_link href={~p"/sources/#{source.id}/edit"} icon="hero-pencil-square" class="mx-1" /> - - +
+ <.table rows={@sources} table_class="text-white" sort_key={@sort_key} sort_direction={@sort_direction}> + <:col :let={source} label="Name" sort_key="custom_name"> + <.subtle_link href={~p"/sources/#{source.id}"}> + {source.custom_name} + + + <:col :let={source} label="Pending" sort_key="pending_count"> + <.subtle_link href={~p"/sources/#{source.id}/#tab-pending"}> + <.localized_number number={source.pending_count} /> + + + <:col :let={source} label="Downloaded" sort_key="downloaded_count"> + <.subtle_link href={~p"/sources/#{source.id}/#tab-downloaded"}> + <.localized_number number={source.downloaded_count} /> + + + <:col :let={source} label="Retention" sort_key="retention_period_days"> + <%= if source.retention_period_days && source.retention_period_days > 0 do %> + <.localized_number number={source.retention_period_days} /> + <.pluralize count={source.retention_period_days} word="day" /> + <% else %> + + <% end %> + + <:col :let={source} label="Media Profile" sort_key="media_profile_name"> + <.subtle_link href={~p"/media_profiles/#{source.media_profile_id}"}> + {source.media_profile.name} + + + <:col :let={source} label="Enabled?" sort_key="enabled"> + <.live_component + module={PinchflatWeb.Sources.SourceLive.SourceEnableToggle} + source={source} + id={"source_#{source.id}_enabled"} + /> + + <:col :let={source} label="" class="flex place-content-evenly"> + <.icon_link href={~p"/sources/#{source.id}/edit"} icon="hero-pencil-square" class="mx-1" /> + + + +
+ <.live_pagination_controls page_number={@page} total_pages={@total_pages} /> +
+
diff --git a/lib/pinchflat_web/controllers/sources/source_live/source_enable_toggle.ex b/lib/pinchflat_web/controllers/sources/source_live/source_enable_toggle.ex index c0f4b65..e8125c2 100644 --- a/lib/pinchflat_web/controllers/sources/source_live/source_enable_toggle.ex +++ b/lib/pinchflat_web/controllers/sources/source_live/source_enable_toggle.ex @@ -8,11 +8,7 @@ defmodule PinchflatWeb.Sources.SourceLive.SourceEnableToggle do ~H"""
<.form :let={f} for={@form} phx-change="update" phx-target={@myself}> - <.input - id={"source_#{@source_id}_enabled_input"} - field={f[:enabled]} - type="toggle" - /> + <.input id={"source_#{@source_id}_enabled_input"} field={f[:enabled]} type="toggle" />
""" diff --git a/lib/pinchflat_web/helpers/pagination_helpers.ex b/lib/pinchflat_web/helpers/pagination_helpers.ex new file mode 100644 index 0000000..1d2e690 --- /dev/null +++ b/lib/pinchflat_web/helpers/pagination_helpers.ex @@ -0,0 +1,30 @@ +defmodule PinchflatWeb.Helpers.PaginationHelpers do + alias Pinchflat.Repo + alias Pinchflat.Utils.NumberUtils + + # TODO: test + def get_pagination_attributes(query, page, records_per_page) do + total_record_count = Repo.aggregate(query, :count, :id) + total_pages = max(ceil(total_record_count / records_per_page), 1) + clamped_page = NumberUtils.clamp(page, 1, total_pages) + + %{ + page: clamped_page, + total_pages: total_pages, + total_record_count: total_record_count, + limit: records_per_page, + offset: (clamped_page - 1) * records_per_page + } + end + + # TODO: test + def update_page_number(current_page, direction, total_pages) do + updated_page = + case to_string(direction) do + "inc" -> current_page + 1 + "dec" -> current_page - 1 + end + + NumberUtils.clamp(updated_page, 1, total_pages) + end +end