diff --git a/lib/music_library/records.ex b/lib/music_library/records.ex
index 6ec84510..70c47051 100644
--- a/lib/music_library/records.ex
+++ b/lib/music_library/records.ex
@@ -4,15 +4,23 @@ defmodule MusicLibrary.Records do
alias MusicLibrary.Records.Record
- def list_records do
+ def list_records(opts \\ []) do
+ limit = Keyword.get(opts, :limit, 20)
+ offset = Keyword.get(opts, :offset, 0)
+
q =
from r in Record,
order_by: r.artists[0]["sort_name"],
- limit: 50
+ limit: ^limit,
+ offset: ^offset
Repo.all(q)
end
+ def count_records do
+ Repo.aggregate(Record, :count)
+ end
+
def get_record!(id), do: Repo.get!(Record, id)
def get_image!(id) do
diff --git a/lib/music_library_web/components/pagination.ex b/lib/music_library_web/components/pagination.ex
index 8e515b42..d0f4f6fb 100644
--- a/lib/music_library_web/components/pagination.ex
+++ b/lib/music_library_web/components/pagination.ex
@@ -4,109 +4,215 @@ defmodule MusicLibraryWeb.Pagination do
# alias Phoenix.LiveView.JS
use Gettext, backend: MusicLibraryWeb.Gettext
- attr :entries, :list, required: true
+ attr :pagination_params, :map, required: true
- def card_footer(assigns) do
- assigns = assign(assigns, page_size: Enum.count(assigns.entries))
+ def pagination(assigns) do
+ page_links = generate_page_links(assigns.pagination_params)
+ assigns = assign(assigns, :page_links, page_links)
~H"""
+ <%!-- Only on smallest viewport --%>
-
-
-
- Showing 1
- to 10
- of <%= @page_size %>
- results
-
-
+
"""
end
+
+ attr :page_number, :integer, required: true
+ attr :page_size, :integer, required: true
+
+ defp next_link(assigns) do
+ ~H"""
+ <.link
+ 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 focus:z-20 focus:outline-offset-0"
+ patch={"?" <> URI.encode_query(page: @page_number, page_size: @page_size)}
+ >
+
Next
+
+
+ """
+ end
+
+ attr :page_number, :integer, required: true
+ attr :page_size, :integer, required: true
+
+ defp prev_link(assigns) do
+ ~H"""
+ <.link
+ 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 focus:z-20 focus:outline-offset-0"
+ patch={"?" <> URI.encode_query(page: @page_number, page_size: @page_size)}
+ >
+
Previous
+
+
+ """
+ end
+
+ defp separator(assigns) do
+ ~H"""
+
+ ...
+
+ """
+ end
+
+ attr :page_number, :integer, required: true
+ attr :page_size, :integer, required: true
+ attr :active, :boolean, default: false
+
+ defp numbered_link(assigns) when assigns.active do
+ ~H"""
+
+ <%= @page_number %>
+
+ """
+ end
+
+ defp numbered_link(assigns) do
+ ~H"""
+ <.link
+ class="relative hidden items-center first:rounded-l-md last:rounded-r-md px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 md:inline-flex"
+ patch={"?" <> URI.encode_query(page: @page_number, page_size: @page_size)}
+ >
+ <%= @page_number %>
+
+ """
+ end
+
+ def get_pagination_params(params, total_records) do
+ %{
+ total_entries: total_records,
+ page: parse_int_or_default(params["page"], 1),
+ page_size: parse_int_or_default(params["page_size"], 20)
+ }
+ end
+
+ def page_to_offset(page, per_page) do
+ (page - 1) * per_page
+ end
+
+ defp parse_int_or_default(nil, default), do: default
+
+ defp parse_int_or_default(value, _default) when is_binary(value) do
+ String.to_integer(value)
+ end
+
+ defp total_pages(total_entries, page_size) do
+ without_remainder = div(total_entries, page_size)
+
+ if rem(total_entries, page_size) == 0 do
+ without_remainder
+ else
+ without_remainder + 1
+ end
+ end
+
+ @visible_left_pages 3
+ @visible_right_pages 3
+ @middle "..."
+ defp generate_page_links(pagination_params) do
+ %{
+ total_entries: total_entries,
+ page: page,
+ page_size: page_size
+ } = pagination_params
+
+ total_pages = total_pages(total_entries, page_size)
+ all_pages = Enum.to_list(1..total_pages)
+
+ {left_pages, middle_pages, right_pages} =
+ Enum.reduce(all_pages, {[], [], []}, fn p, {left_p, middle_p, right_p} ->
+ cond do
+ p < page - 2 ->
+ {left_p ++ [p], middle_p, right_p}
+
+ p > page + 2 ->
+ {left_p, middle_p, right_p ++ [p]}
+
+ true ->
+ {left_p, middle_p ++ [p], right_p}
+ end
+ end)
+
+ prev_page = if page > 1, do: page - 1
+ next_page = if page < total_pages, do: page + 1
+
+ left_separator = if Enum.count(left_pages) > @visible_left_pages, do: @middle
+ right_separator = if Enum.count(right_pages) > @visible_right_pages, do: @middle
+
+ visible_left_pages = Enum.take(left_pages, @visible_left_pages)
+ visible_right_pages = Enum.take(right_pages, -@visible_right_pages)
+
+ %{
+ total_pages: total_pages,
+ prev_page: prev_page,
+ visible_left_pages: visible_left_pages,
+ left_separator: left_separator,
+ middle_pages: middle_pages,
+ right_separator: right_separator,
+ visible_right_pages: visible_right_pages,
+ next_page: next_page
+ }
+ end
end
diff --git a/lib/music_library_web/live/record_live/index.ex b/lib/music_library_web/live/record_live/index.ex
index 8032d3e0..ab2b8e28 100644
--- a/lib/music_library_web/live/record_live/index.ex
+++ b/lib/music_library_web/live/record_live/index.ex
@@ -6,8 +6,17 @@ defmodule MusicLibraryWeb.RecordLive.Index do
alias MusicLibrary.Records.Record
@impl true
- def mount(_params, _session, socket) do
- {:ok, stream(socket, :records, Records.list_records())}
+ def mount(params, _session, socket) do
+ total_records = Records.count_records()
+
+ pagination_params = get_pagination_params(params, total_records)
+ offset = page_to_offset(pagination_params.page, pagination_params.page_size)
+ records = Records.list_records(limit: pagination_params.page_size, offset: offset)
+
+ {:ok,
+ socket
+ |> assign(:pagination_params, pagination_params)
+ |> stream(:records, records)}
end
@impl true
@@ -27,10 +36,25 @@ defmodule MusicLibraryWeb.RecordLive.Index do
|> assign(:record, %Record{})
end
- defp apply_action(socket, :index, _params) do
- socket
- |> assign(:page_title, "Listing Records")
- |> assign(:record, nil)
+ defp apply_action(socket, :index, params) do
+ new_socket =
+ socket
+ |> assign(:page_title, "Listing Records")
+ |> assign(:record, nil)
+
+ total_records = Records.count_records()
+ pagination_params = get_pagination_params(params, total_records)
+
+ if pagination_params != socket.assigns.pagination_params do
+ offset = page_to_offset(pagination_params.page, pagination_params.page_size)
+ records = Records.list_records(limit: pagination_params.page_size, offset: offset)
+
+ new_socket
+ |> assign(:pagination_params, pagination_params)
+ |> stream(:records, records, reset: true)
+ else
+ new_socket
+ end
end
@impl true
diff --git a/lib/music_library_web/live/record_live/index.html.heex b/lib/music_library_web/live/record_live/index.html.heex
index 0088ae88..398a405a 100644
--- a/lib/music_library_web/live/record_live/index.html.heex
+++ b/lib/music_library_web/live/record_live/index.html.heex
@@ -55,4 +55,4 @@
/>
-<.card_footer entries={@streams.records} />
+<.pagination pagination_params={@pagination_params} />