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 "faker", "~> 3.5"
gem "kaminari", "~> 1.2"
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
# gem "bcrypt", "~> 3.1.7"

View File

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

View File

@ -4,7 +4,9 @@ class UsersController < ApplicationController
before_action :correct_user, only: [ :edit, :update ]
def index
@users = User.all
# @users = User.all
# @users = User.order(:name).page(params[:page])
@users = User.page(params[:page])
end
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>
<%= paginate @users %>
<ul class="users">
<% @users.each do |user| %>
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
</li>
<% end %>
</ul>
<%= render @users %>
<%# auto loop by rails, like after %>
<%# @users.each do |user| %>
<!-- <li>-->
<%#= gravatar_for user, size: 50 %>
<%#= link_to user.name, user %>
<!-- </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"
en:
hello: "Hello world"
hello: "Hello world"

View File

@ -7,3 +7,18 @@
# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
# MovieGenre.find_or_create_by!(name: genre_name)
# 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:
name: Sterling Archer
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_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", users_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?