diff --git a/lib/beacon/live_admin/components/admin_components.ex b/lib/beacon/live_admin/components/admin_components.ex
index 4271b750..98adde90 100644
--- a/lib/beacon/live_admin/components/admin_components.ex
+++ b/lib/beacon/live_admin/components/admin_components.ex
@@ -621,4 +621,23 @@ defmodule Beacon.LiveAdmin.AdminComponents do
"""
end
+
+ @doc """
+ Renders navigation by defined pagination.
+
+ """
+ attr :current_page, :integer, required: true
+ attr :pages, :integer, required: true
+
+ def pagination(assigns) do
+ ~H"""
+
+
+
+
+
+ """
+ end
end
diff --git a/lib/beacon/live_admin/content.ex b/lib/beacon/live_admin/content.ex
index 82fa2db8..72b27041 100644
--- a/lib/beacon/live_admin/content.ex
+++ b/lib/beacon/live_admin/content.ex
@@ -94,14 +94,13 @@ defmodule Beacon.LiveAdmin.Content do
end
def list_pages(site, opts \\ []) do
- opts =
- opts
- |> Keyword.put_new(:query, nil)
- |> Keyword.put_new(:per_page, 20)
-
call(site, Beacon.Content, :list_pages, [site, opts])
end
+ def count_pages(site) do
+ call(site, Beacon.Content, :count_pages, [site])
+ end
+
def change_page_variant(site, variant, attrs \\ %{}) do
call(site, Beacon.Content, :change_page_variant, [variant, attrs])
end
diff --git a/lib/beacon/live_admin/live/page_editor_live/index.ex b/lib/beacon/live_admin/live/page_editor_live/index.ex
index 16d9bea7..5ecd60af 100644
--- a/lib/beacon/live_admin/live/page_editor_live/index.ex
+++ b/lib/beacon/live_admin/live/page_editor_live/index.ex
@@ -6,35 +6,95 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
on_mount {Beacon.LiveAdmin.Hooks.Authorized, {:page_editor, :index}}
+ @per_page 20
+ @default_sort :title
+
@impl true
def menu_link(_, :index), do: {:root, "Pages"}
@impl true
def mount(_params, _session, socket) do
- {:ok, assign(socket, :pages, [])}
+ {:ok,
+ socket
+ |> assign(
+ page: 1,
+ pages: number_of_pages(socket.assigns.beacon_page.site),
+ sort: @default_sort,
+ query: ""
+ )
+ |> stream_configure(:pages, dom_id: &"#{Ecto.UUID.generate()}-#{&1.id}")}
end
@impl true
- def handle_params(%{"query" => query}, _uri, socket) do
- pages = list_pages(socket.assigns.beacon_page.site, query: query)
- {:noreply, assign(socket, :pages, pages)}
- end
+ def handle_params(params, _uri, socket) do
+ query = params["query"]
+ offset = set_offset(params["page"])
+ sort = set_sort(params["sort"], socket)
+ socket = set_page(offset, params["page"], socket)
+
+ pages =
+ list_pages(socket.assigns.beacon_page.site,
+ per_page: @per_page,
+ offset: offset,
+ query: query,
+ sort: sort
+ )
- def handle_params(_params, _uri, socket) do
- pages = list_pages(socket.assigns.beacon_page.site)
- {:noreply, assign(socket, :pages, pages)}
+ {:noreply,
+ socket
+ |> assign(sort: sort, query: query)
+ |> stream(:pages, pages, reset: true)}
end
@impl true
- def handle_event("search", %{"search" => %{"query" => query}}, socket) do
+ def handle_event("search", %{"search" => %{"query" => query, "sort" => sort}}, socket) do
+ path =
+ beacon_live_admin_path(
+ socket,
+ socket.assigns.beacon_page.site,
+ "/pages?page=#{socket.assigns.page}&sort=#{sort}#{query_param(query)}"
+ )
+
+ {:noreply,
+ socket
+ |> assign(sort: set_sort(sort, socket))
+ |> push_patch(to: path)}
+ end
+
+ def handle_event("set-page", %{"page" => page}, socket) do
+ page
+ |> String.to_integer()
+ |> set_page(socket)
+ end
+
+ def handle_event("prev-page", _, %{assigns: %{page: 1}} = socket) do
+ {:noreply, socket}
+ end
+
+ def handle_event("prev-page", _, socket) do
+ set_page(socket.assigns.page - 1, socket)
+ end
+
+ def handle_event("next-page", _, %{assigns: %{page: page, pages: page}} = socket) do
+ {:noreply, socket}
+ end
+
+ def handle_event("next-page", _, socket) do
+ set_page(socket.assigns.page + 1, socket)
+ end
+
+ defp set_page(page, socket) do
path =
beacon_live_admin_path(
socket,
socket.assigns.beacon_page.site,
- "/pages?query=#{query}"
+ "/pages?page=#{page}"
)
- {:noreply, push_patch(socket, to: path)}
+ {:noreply,
+ socket
+ |> assign(page: page)
+ |> push_patch(to: path)}
end
@impl true
@@ -50,15 +110,22 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
<.simple_form :let={f} for={%{}} as={:search} phx-change="search">
- <.input field={f[:query]} type="search" autofocus={true} placeholder="Search by path or title (showing up to 20 results)" />
+
+
+ <.input field={f[:query]} value={@query} type="search" autofocus={true} placeholder="Search by path or title (showing up to 20 results)" />
+
+
0} class="basis-1/12">
+ <.input type="select" field={f[:sort]} value={@sort} options={[{"Title", "title"}, {"Path", "path"}]} />
+
+
<.main_content class="h-[calc(100vh_-_210px)]">
- <.table id="pages" rows={@pages} row_click={fn page -> JS.navigate(beacon_live_admin_path(@socket, @beacon_page.site, "/pages/#{page.id}")) end}>
- <:col :let={page} label="Title"><%= page.title %>
- <:col :let={page} label="Path"><%= page.path %>
- <:col :let={page} label="Status"><%= display_status(page.status) %>
- <:action :let={page}>
+ <.table id="pages" rows={@streams.pages} row_click={fn {_dom_id, page} -> JS.navigate(beacon_live_admin_path(@socket, @beacon_page.site, "/pages/#{page.id}")) end}>
+ <:col :let={{_, page}} label="Title"><%= page.title %>
+ <:col :let={{_, page}} label="Path"><%= page.path %>
+ <:col :let={{_, page}} label="Status"><%= display_status(page.status) %>
+ <:action :let={{_, page}}>
<.link navigate={beacon_live_admin_path(@socket, @beacon_page.site, "/pages/#{page.id}")}>Show
@@ -67,11 +134,13 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
+
+ <.pagination :if={@pages > 0} current_page={@page} pages={@pages} />
"""
end
- defp list_pages(site, opts \\ []) do
+ defp list_pages(site, opts) do
site
|> Content.list_pages(opts)
|> Enum.map(fn page ->
@@ -79,7 +148,28 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
end)
end
+ defp number_of_pages(site) do
+ site
+ |> Content.count_pages()
+ |> Kernel./(@per_page)
+ |> ceil()
+ end
+
+ defp set_offset(nil), do: 0
+ defp set_offset(page) when is_binary(page), do: String.to_integer(page) * @per_page - @per_page
+ defp set_offset(page), do: page * @per_page - @per_page
+
+ defp set_page(0, _page, socket), do: socket
+ defp set_page(_offset, page, socket), do: assign(socket, page: String.to_integer(page))
+
+ defp set_sort(nil, socket), do: socket.assigns.sort
+ defp set_sort("", socket), do: socket.assigns.sort
+ defp set_sort(sort, _socket), do: String.to_atom(sort)
+
defp display_status(:unpublished), do: "Unpublished"
defp display_status(:published), do: "Published"
defp display_status(:created), do: "Draft"
+
+ defp query_param(""), do: ""
+ defp query_param(query), do: "&query=#{query}"
end
diff --git a/mix.lock b/mix.lock
index ff67215c..18a44c6a 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,6 +1,6 @@
%{
"accent": {:hex, :accent, "1.1.1", "20257356446d45078b19b91608f74669b407b39af891ee3db9ee6824d1cae19d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.3", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "6d5afa50d4886e3370e04fa501468cbaa6c4b5fe926f72ccfa844ad9e259adae"},
- "beacon": {:git, "https://github.com/beaconCMS/beacon.git", "4fa1eca70e84ca7157b0e5dcbb4cc60f9f10b25f", []},
+ "beacon": {:git, "https://github.com/beaconCMS/beacon.git", "3edd59790e8d28b9d1610203f41396d3bf434015", []},
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.7", "77de20ac77f0e53f20ca82c563520af0237c301a1ec3ab3bc598e8a96c7ee5d9", [:mix], [{:elixir_make, "~> 0.7.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2768b28bf3c2b4f788c995576b39b8cb5d47eb788526d93bd52206c1d8bf4b75"},
"cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"},