diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a37f73f66c22604ffd29c7f25b08b7046bb22c4..47b7b0564e9b9386028ad4d9d526c6f384a12c31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fix weekly report time range plausible/analytics#951 - Make sure embedded dashboards can run when user has blocked third-party cookies plausible/analytics#971 +- Sites listing page will paginate if the user has a lot of sites plausible/analytics#994 ### Removed - Removes AppSignal monitoring package diff --git a/assets/css/app.css b/assets/css/app.css index 11a7168f373eca5de1cc6a972e546686f6b59966..59cabe8827a8572b5ab698bd69a9eb8bc883237a 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -304,3 +304,7 @@ blockquote { iframe[hidden] { display: none; } + +.pagination-link[disabled] { + @apply cursor-default bg-gray-100 pointer-events-none; +} diff --git a/lib/plausible/repo.ex b/lib/plausible/repo.ex index 2f9147e662e0ef0ad2bd3bc510a8cae0f650c1fa..6534885210b9f70755a5e847fb577411787bd676 100644 --- a/lib/plausible/repo.ex +++ b/lib/plausible/repo.ex @@ -3,6 +3,8 @@ defmodule Plausible.Repo do otp_app: :plausible, adapter: Ecto.Adapters.Postgres + use Phoenix.Pagination, per_page: 18 + defmacro __using__(_) do quote do alias Plausible.Repo diff --git a/lib/plausible_web/controllers/site_controller.ex b/lib/plausible_web/controllers/site_controller.ex index 13252eebb288e45a17ac47ba6d134c860973b171..0599ed92925f65f4a5f0d0243b44f191fd74642e 100644 --- a/lib/plausible_web/controllers/site_controller.ex +++ b/lib/plausible_web/controllers/site_controller.ex @@ -5,20 +5,22 @@ defmodule PlausibleWeb.SiteController do plug PlausibleWeb.RequireAccountPlug - def index(conn, _params) do + def index(conn, params) do user = conn.assigns[:current_user] - sites = - Repo.all( - from s in Plausible.Site, + {sites, pagination} = + Repo.paginate( + from(s in Plausible.Site, join: sm in Plausible.Site.Membership, on: sm.site_id == s.id, where: sm.user_id == ^user.id, order_by: s.domain + ), + params ) visitors = Plausible.Stats.Clickhouse.last_24h_visitors(sites) - render(conn, "index.html", sites: sites, visitors: visitors) + render(conn, "index.html", sites: sites, visitors: visitors, pagination: pagination) end def new(conn, _params) do diff --git a/lib/plausible_web/templates/site/index.html.eex b/lib/plausible_web/templates/site/index.html.eex index eed01ccb29e9428295b7281e8861770eddad0428..192ed458809f84c1769d88ce5e9cfd6cbf8ce12b 100644 --- a/lib/plausible_web/templates/site/index.html.eex +++ b/lib/plausible_web/templates/site/index.html.eex @@ -37,4 +37,25 @@ </div> <% end %> </ul> + + <%= if @pagination.total_pages > 1 do %> + <%= pagination @conn, @pagination, [current_class: "is-current"], fn p -> %> + <nav class="px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6" aria-label="Pagination"> + <div class="hidden sm:block"> + <p class="text-sm text-gray-700"> + Showing page + <span class="font-medium"><%= @pagination.page %></span> + of + <span class="font-medium"><%= @pagination.total_pages %></span> + total + </p> + </div> + <div class="flex-1 flex justify-between sm:justify-end"> + <%= pagination_link(p, :previous, label: "← Previous", class: "pagination-link relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50", force_show: true) %> + <%= pagination_link(p, :next, label: "Next →", class: "pagination-link ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50", force_show: true) %> + </div> + </nav> + <% end %> + <% end %> + </div> diff --git a/lib/plausible_web/views/site_view.ex b/lib/plausible_web/views/site_view.ex index 5fd8dfcaeb94512e7b6e0046323e773a200a48df..40c553c4629f781de82653e7f21162fd18a26f5b 100644 --- a/lib/plausible_web/views/site_view.ex +++ b/lib/plausible_web/views/site_view.ex @@ -1,5 +1,6 @@ defmodule PlausibleWeb.SiteView do use PlausibleWeb, :view + import Phoenix.Pagination.HTML def admin_email do Application.get_env(:plausible, :admin_email) diff --git a/mix.exs b/mix.exs index 7eb6af5be3586e89e10dedfa1c69edd50a08fb10..f6e3b9aef38fc9bad545bb80601dfcbfe7bbd2fb 100644 --- a/mix.exs +++ b/mix.exs @@ -94,7 +94,8 @@ defmodule Plausible.MixProject do {:credo, "~> 1.5", only: [:dev, :test], runtime: false}, {:kaffy, "~> 0.9.0"}, {:envy, "~> 1.1.1"}, - {:ink, "~> 1.0"} + {:ink, "~> 1.0"}, + {:phoenix_pagination, "~> 0.7.0"} ] end diff --git a/mix.lock b/mix.lock index 273a6aab17ae9bb93bc450a5ecf9579214d3d5a5..2abdce41cb6d91cea0c6e5d3941d0c31f558ba8f 100644 --- a/mix.lock +++ b/mix.lock @@ -65,6 +65,7 @@ "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"}, "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.0", "f35f61c3f959c9a01b36defaa1f0624edd55b87e236b606664a556d6f72fd2e7", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "02c1007ae393f2b76ec61c1a869b1e617179877984678babde131d716f95b582"}, + "phoenix_pagination": {:hex, :phoenix_pagination, "0.7.0", "e8503270da3c41f4ac4fea5ae90503f51287e9cd72b3a6abb0c547fe84d9639b", [:mix], [{:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.12", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.11.1", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "27055338e9824bb302bb0d72e14972a9a2fb916bf435545f04f361671d6d827f"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, "php_serializer": {:hex, :php_serializer, "0.9.2", "59c5fd6bd3096671fd89358fb8229341ac7423b50ad8d45a15213b02ea2edab2", [:mix], [], "hexpm", "34eb835a460944f7fc216773b363c02e7dcf8ac0390c9e9ccdbd92b31a7ca59a"}, "plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"}, diff --git a/test/plausible_web/controllers/site_controller_test.exs b/test/plausible_web/controllers/site_controller_test.exs index b79324302c4ccbcbf262c40303e5029eb9100c5e..52b139c0a75644f917011f44bd356f222b70b763 100644 --- a/test/plausible_web/controllers/site_controller_test.exs +++ b/test/plausible_web/controllers/site_controller_test.exs @@ -43,6 +43,27 @@ defmodule PlausibleWeb.SiteControllerTest do assert html_response(conn, 200) =~ "test-site.com" assert html_response(conn, 200) =~ "<b>3</b> visitors in last 24h" end + + test "paginates sites", %{conn: conn, user: user} do + insert(:site, members: [user], domain: "test-site1.com") + insert(:site, members: [user], domain: "test-site2.com") + insert(:site, members: [user], domain: "test-site3.com") + insert(:site, members: [user], domain: "test-site4.com") + + conn = get(conn, "/sites?per_page=2") + + assert html_response(conn, 200) =~ "test-site1.com" + assert html_response(conn, 200) =~ "test-site2.com" + refute html_response(conn, 200) =~ "test-site3.com" + refute html_response(conn, 200) =~ "test-site4.com" + + conn = get(conn, "/sites?per_page=2&page=2") + + refute html_response(conn, 200) =~ "test-site1.com" + refute html_response(conn, 200) =~ "test-site2.com" + assert html_response(conn, 200) =~ "test-site3.com" + assert html_response(conn, 200) =~ "test-site4.com" + end end describe "POST /sites" do