feat: add pagination to user index view

- Integrate Kaminari for pagination of users
- Update users_controller to paginate users
- Add pagination views for first, last, next, and previous pages
- Seed database with example users for testing

This commit introduces pagination to the user index view, allowing
for better navigation through large sets of users. The Kaminari gem
is utilized to handle pagination, improving the user experience by
reducing load times and enhancing usability. Additionally, the
seeding script has been updated to create multiple users for
better testing of the pagination feature.
This commit is contained in:
songtianlun 2025-01-05 17:50:33 +08:00
parent 5b7f75b5a8
commit 022eae3029
18 changed files with 134 additions and 10 deletions

View File

@ -19,6 +19,9 @@ gem "jbuilder"
gem "bcrypt", "~> 3.1" gem "bcrypt", "~> 3.1"
gem "faker", "~> 3.5"
gem "kaminari", "~> 1.2"
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
# gem "bcrypt", "~> 3.1.7" # gem "bcrypt", "~> 3.1.7"

View File

@ -114,6 +114,8 @@ GEM
erubi (1.13.1) erubi (1.13.1)
et-orbi (1.2.11) et-orbi (1.2.11)
tzinfo tzinfo
faker (3.5.1)
i18n (>= 1.8.11, < 2)
ffi (1.17.1-aarch64-linux-gnu) ffi (1.17.1-aarch64-linux-gnu)
ffi (1.17.1-aarch64-linux-musl) ffi (1.17.1-aarch64-linux-musl)
ffi (1.17.1-arm-linux-gnu) ffi (1.17.1-arm-linux-gnu)
@ -166,6 +168,18 @@ GEM
sshkit (>= 1.23.0, < 2.0) sshkit (>= 1.23.0, < 2.0)
thor (~> 1.3) thor (~> 1.3)
zeitwerk (>= 2.6.18, < 3.0) zeitwerk (>= 2.6.18, < 3.0)
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
kaminari-activerecord (= 1.2.2)
kaminari-core (= 1.2.2)
kaminari-actionview (1.2.2)
actionview
kaminari-core (= 1.2.2)
kaminari-activerecord (1.2.2)
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
language_server-protocol (3.17.0.3) language_server-protocol (3.17.0.3)
listen (3.9.0) listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
@ -420,11 +434,13 @@ DEPENDENCIES
capybara capybara
cssbundling-rails (~> 1.4) cssbundling-rails (~> 1.4)
debug debug
faker (~> 3.5)
guard (~> 2.19) guard (~> 2.19)
importmap-rails importmap-rails
jbuilder jbuilder
jsbundling-rails (~> 1.3) jsbundling-rails (~> 1.3)
kamal kamal
kaminari (~> 1.2)
minitest-reporters minitest-reporters
propshaft propshaft
puma (>= 5.0) puma (>= 5.0)

View File

@ -4,7 +4,9 @@ class UsersController < ApplicationController
before_action :correct_user, only: [ :edit, :update ] before_action :correct_user, only: [ :edit, :update ]
def index def index
@users = User.all # @users = User.all
# @users = User.order(:name).page(params[:page])
@users = User.page(params[:page])
end end
def show def show

View File

@ -0,0 +1,3 @@
<li>
<%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote %>
</li>

View File

@ -0,0 +1,3 @@
<li class='disabled'>
<%= content_tag :a, raw(t 'views.pagination.truncate') %>
</li>

View File

@ -0,0 +1,3 @@
<li>
<%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, { remote: remote } %>
</li>

View File

@ -0,0 +1,3 @@
<li>
<%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote %>
</li>

View File

@ -0,0 +1,9 @@
<% if page.current? %>
<li class='active'>
<%= content_tag :a, page, data: { remote: remote }, rel: page.rel %>
</li>
<% else %>
<li>
<%= link_to page, url, remote: remote, rel: page.rel %>
</li>
<% end %>

View File

@ -0,0 +1,15 @@
<%= paginator.render do -%>
<ul class="pagination">
<%= first_page_tag unless current_page.first? %>
<%= prev_page_tag unless current_page.first? %>
<% each_page do |page| -%>
<% if page.left_outer? || page.right_outer? || page.inside_window? -%>
<%= page_tag page %>
<% elsif !page.was_truncated? -%>
<%= gap_tag %>
<% end -%>
<% end -%>
<%= next_page_tag unless current_page.last? %>
<%= last_page_tag unless current_page.last? %>
</ul>
<% end -%>

View File

@ -0,0 +1,3 @@
<li>
<%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote %>
</li>

View File

@ -0,0 +1,4 @@
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
</li>

View File

@ -2,11 +2,16 @@
<h1>All users</h1> <h1>All users</h1>
<%= paginate @users %>
<ul class="users"> <ul class="users">
<% @users.each do |user| %> <%= render @users %>
<li> <%# auto loop by rails, like after %>
<%= gravatar_for user, size: 50 %> <%# @users.each do |user| %>
<%= link_to user.name, user %> <!-- <li>-->
</li> <%#= gravatar_for user, size: 50 %>
<% end %> <%#= link_to user.name, user %>
</ul> <!-- </li>-->
<%# end %>
</ul>

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
Kaminari.configure do |config|
config.default_per_page = 5
# config.default_per_page = 25
# config.max_per_page = nil
# config.window = 4
# config.outer_window = 0
# config.left = 0
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
# config.max_pages = nil
# config.params_on_first_page = false
end

View File

@ -28,4 +28,4 @@
# enabled: "ON" # enabled: "ON"
en: en:
hello: "Hello world" hello: "Hello world"

View File

@ -7,3 +7,18 @@
# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| # ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
# MovieGenre.find_or_create_by!(name: genre_name) # MovieGenre.find_or_create_by!(name: genre_name)
# end # end
User.create!(name: "Example User",
email: "example@example.com",
password: "foobar",
password_confirmation: "foobar")
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}@railstutorial.org"
password = "password"
User.create!(name: name,
email: email,
password: password,
password_confirmation: password)
end

View File

@ -12,4 +12,11 @@ michael:
archer: archer:
name: Sterling Archer name: Sterling Archer
email: suchess@example.gov email: suchess@example.gov
password_digest: <%= User.digest('password') %> password_digest: <%= User.digest('password') %>
<% 30.times do |n| %>
user_<%= n %>:
name: <%= "user #{n}" %>
email: <%= "user-#{n}@example.com" %>
password_digest: <%= User.digest('password') %>
<% end %>

View File

@ -0,0 +1,17 @@
require "test_helper"
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "index including pagination" do
log_in_as(@user)
get users_path
assert_template "users/index"
assert_select "ul.pagination"
User.page(1).each do |user|
assert_select "a[href=?]", user_path(user), text: user.name
end
end
end

View File

@ -39,6 +39,7 @@ class UsersLoginTest < ActionDispatch::IntegrationTest
assert_template "users/show" assert_template "users/show"
assert_select "a[href=?]", login_path, count: 0 assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path assert_select "a[href=?]", logout_path
assert_select "a[href=?]", users_path
assert_select "a[href=?]", user_path(@user) assert_select "a[href=?]", user_path(@user)
delete logout_path delete logout_path
assert_not is_logged_in? assert_not is_logged_in?