Skip to content

Commit

Permalink
Add a users index with scopes and batch deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
elia committed Nov 27, 2023
1 parent b013fdc commit d53ab84
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 0 deletions.
34 changes: 34 additions & 0 deletions admin/app/components/solidus_admin/users/index/component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<%= page do %>
<%= page_header do %>
<%= page_header_title title %>
<%= page_header_actions do %>
<%= render component("ui/button").new(
tag: :a,
text: t('.add'),
href: spree.new_admin_user_path,
icon: "add-line",
) %>
<% end %>
<% end %>

<%= render component('ui/table').new(
id: stimulus_id,
data: {
class: Spree.user_class,
rows: @page.records,
url: ->(user) { spree.admin_user_path(user) },
prev: prev_page_link,
next: next_page_link,
columns: columns,
batch_actions: batch_actions,
},
search: {
name: :q,
value: params[:q],
url: solidus_admin.users_path,
searchbar_key: :email_cont,
filters: filters,
scopes: scopes,
},
) %>
<% end %>
89 changes: 89 additions & 0 deletions admin/app/components/solidus_admin/users/index/component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# frozen_string_literal: true

class SolidusAdmin::Users::Index::Component < SolidusAdmin::BaseComponent
include SolidusAdmin::Layout::PageHelpers

def initialize(page:)
@page = page
end

def title
Spree.user_class.model_name.human.pluralize
end

def prev_page_link
@page.first? ? nil : solidus_admin.url_for(host: request.host, port: request.port, **request.params, page: @page.number - 1)
end

def next_page_link
@page.last? ? nil : solidus_admin.url_for(host: request.host, port: request.port, **request.params, page: @page.next_param)
end

def batch_actions
[
{
display_name: t('.batch_actions.delete'),
action: solidus_admin.users_path,
method: :delete,
icon: 'delete-bin-7-line',
},
]
end

def filters
[
{
presentation: Spree::Role.model_name.human.pluralize,
attribute: "spree_roles_id",
predicate: "in",
options: Spree::Role.pluck(:name, :id)
}
]
end

def scopes
[
{ name: :customers, label: t('.scopes.customers'), default: true },
{ name: :admin, label: t('.scopes.admin') },
{ name: :with_orders, label: t('.scopes.with_orders') },
{ name: :without_orders, label: t('.scopes.without_orders') },
{ name: :all, label: t('.scopes.all') },
]
end

def columns
[
{
header: :email,
data: :email,
},
{
header: :roles,
data: ->(user) do
roles = user.spree_roles.presence || [Spree::Role.new(name: 'customer')]
safe_join(roles.map {
color =
case _1.name
when 'admin' then :blue
when 'customer' then :green
else :graphite_light
end
render component('ui/badge').new(name: _1.name, color: color)
})
end,
},
{
header: :order_count,
data: ->(user) { user.order_count },
},
{
header: :lifetime_value,
data: -> { _1.display_lifetime_value.to_html },
},
{
header: :created_at,
data: ->(user) { l(user.created_at.to_date, format: :long) },
},
]
end
end
18 changes: 18 additions & 0 deletions admin/app/components/solidus_admin/users/index/component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
en:
promotion_image: 'Image'
add: 'Add Promotion'
batch_actions:
delete: 'Delete'
discontinue: 'Discontinue'
activate: 'Activate'
filters:
with_deleted: Include deleted
scopes:
customers: Customers
admin: Admins
with_orders: With Orders
without_orders: Without Orders
all: All
status:
active: Active
inactive: Inactive
50 changes: 50 additions & 0 deletions admin/app/controllers/solidus_admin/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module SolidusAdmin
class UsersController < SolidusAdmin::BaseController
include SolidusAdmin::ControllerHelpers::Search

search_scope(:customers, default: true) { _1.left_outer_joins(:role_users).where(role_users: { id: nil }) }
search_scope(:admin) { _1.joins(:role_users).distinct }
search_scope(:with_orders) { _1.joins(:orders).distinct }
search_scope(:without_orders) { _1.left_outer_joins(:orders).where(orders: { id: nil }) }
search_scope(:all)

def index
users = apply_search_to(
Spree.user_class.order(created_at: :desc, id: :desc),
param: :q,
)

set_page_and_extract_portion_from(users)

respond_to do |format|
format.html { render component('users/index').new(page: @page) }
end
end

def destroy
@users = Spree.user_class.where(id: params[:id])

Spree.user_class.transaction { @users.destroy_all }

flash[:notice] = t('.success')
redirect_back_or_to users_path, status: :see_other
end

private

def load_user
@user = Spree.user_class.find_by!(number: params[:id])
authorize! action_name, @user
end

def user_params
params.require(:user).permit(:user_id, permitted_user_attributes)
end

def authorization_subject
Spree.user_class
end
end
end
6 changes: 6 additions & 0 deletions admin/config/locales/users.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
en:
solidus_admin:
users:
title: "Users"
destroy:
success: "Users were successfully removed."
6 changes: 6 additions & 0 deletions admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@
get :customers_for
end
end

resources :users, only: [:index] do
collection do
delete :destroy
end
end
end
34 changes: 34 additions & 0 deletions admin/spec/features/users_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

require 'spec_helper'

describe "Users", :js, type: :feature do
before { sign_in create(:admin_user, email: 'admin@example.com') }

it "lists users and allows deleting them" do
create(:user, email: "customer@example.com")
create(:admin_user, email: "admin-2@example.com")
create(:user, :with_orders, email: "customer-with-order@example.com")

visit "/admin/users"
expect(page).to have_content("customer@example.com")
expect(page).not_to have_content("admin-2@example.com")
click_on "Admins"
expect(page).to have_content("admin-2@example.com")
expect(page).not_to have_content("customer@example.com")
click_on "With Orders"
expect(page).to have_content("customer-with-order@example.com")
click_on "All"
expect(page).to have_content("customer@example.com")
expect(page).to have_content("admin-2@example.com")
expect(page).to have_content("customer-with-order@example.com")

expect(page).to be_axe_clean

select_row("customer@example.com")
click_on "Delete"
expect(page).to have_content("Users were successfully removed.")
expect(page).not_to have_content("customer@example.com")
expect(Spree.user_class.count).to eq(3)
end
end

0 comments on commit d53ab84

Please sign in to comment.