From 6f6f44cff5466b9c8395c4fdd141384f330c4fdd Mon Sep 17 00:00:00 2001 From: Elia Schito Date: Mon, 27 Nov 2023 14:31:56 +0100 Subject: [PATCH 1/3] Simplify the next/prev page path helpers in index components --- .../solidus_admin/orders/index/component.html.erb | 4 ++-- .../components/solidus_admin/orders/index/component.rb | 8 ++++---- .../solidus_admin/products/index/component.html.erb | 4 ++-- .../components/solidus_admin/products/index/component.rb | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/admin/app/components/solidus_admin/orders/index/component.html.erb b/admin/app/components/solidus_admin/orders/index/component.html.erb index 5a0440033ac..48d50d77e53 100644 --- a/admin/app/components/solidus_admin/orders/index/component.html.erb +++ b/admin/app/components/solidus_admin/orders/index/component.html.erb @@ -23,8 +23,8 @@ url: ->(order) { spree.edit_admin_order_path(order) }, batch_actions: batch_actions, columns: columns, - prev: prev_page_link, - next: next_page_link, + prev: prev_page_path, + next: next_page_path, }, search: { name: :q, diff --git a/admin/app/components/solidus_admin/orders/index/component.rb b/admin/app/components/solidus_admin/orders/index/component.rb index 407e5b710fa..e1d4e39c523 100644 --- a/admin/app/components/solidus_admin/orders/index/component.rb +++ b/admin/app/components/solidus_admin/orders/index/component.rb @@ -11,12 +11,12 @@ def title Spree::Order.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) + def prev_page_path + solidus_admin.url_for(**request.params, page: @page.number - 1, only_path: true) unless @page.first? end - def next_page_link - @page.last? ? nil : solidus_admin.url_for(host: request.host, port: request.port, **request.params, page: @page.next_param) + def next_page_path + solidus_admin.url_for(**request.params, page: @page.next_param, only_path: true) unless @page.last? end def batch_actions diff --git a/admin/app/components/solidus_admin/products/index/component.html.erb b/admin/app/components/solidus_admin/products/index/component.html.erb index f3033efaeb2..ab9cb3c6880 100644 --- a/admin/app/components/solidus_admin/products/index/component.html.erb +++ b/admin/app/components/solidus_admin/products/index/component.html.erb @@ -17,8 +17,8 @@ class: Spree::Product, rows: @page.records, url: ->(product) { solidus_admin.product_path(product) }, - prev: prev_page_link, - next: next_page_link, + prev: prev_page_path, + next: next_page_path, columns: columns, batch_actions: batch_actions, }, diff --git a/admin/app/components/solidus_admin/products/index/component.rb b/admin/app/components/solidus_admin/products/index/component.rb index a53adb8ce7e..61ed95110fd 100644 --- a/admin/app/components/solidus_admin/products/index/component.rb +++ b/admin/app/components/solidus_admin/products/index/component.rb @@ -11,12 +11,12 @@ def title Spree::Product.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) + def prev_page_path + solidus_admin.url_for(**request.params, page: @page.number - 1, only_path: true) unless @page.first? end - def next_page_link - @page.last? ? nil : solidus_admin.url_for(host: request.host, port: request.port, **request.params, page: @page.next_param) + def next_page_path + solidus_admin.url_for(**request.params, page: @page.next_param, only_path: true) unless @page.last? end def batch_actions From b013fdc43520f89051be0a25cb8db581278f3da0 Mon Sep 17 00:00:00 2001 From: Elia Schito Date: Mon, 27 Nov 2023 12:41:16 +0100 Subject: [PATCH 2/3] Extract row helpers form promotions and products --- admin/spec/features/products_spec.rb | 2 +- admin/spec/support/solidus_admin/feature_helpers.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/admin/spec/features/products_spec.rb b/admin/spec/features/products_spec.rb index 0a4cf7a6424..b390e4c24bb 100644 --- a/admin/spec/features/products_spec.rb +++ b/admin/spec/features/products_spec.rb @@ -25,7 +25,7 @@ create(:product, name: "Another product", price: 29.99) visit "/admin/products" - find('main tbody tr:nth-child(2)').find('input').check + select_row("Just a product") click_button "Delete" expect(page).to have_content("Products were successfully removed.", wait: 5) diff --git a/admin/spec/support/solidus_admin/feature_helpers.rb b/admin/spec/support/solidus_admin/feature_helpers.rb index 8754f33ee67..0f21093a108 100644 --- a/admin/spec/support/solidus_admin/feature_helpers.rb +++ b/admin/spec/support/solidus_admin/feature_helpers.rb @@ -16,5 +16,17 @@ def stub_authorization!(user) allow_any_instance_of(SolidusAdmin::BaseController).to receive(:current_ability).and_return(ability) allow_any_instance_of(Spree::Admin::BaseController).to receive(:current_ability).and_return(ability) end + + def find_row(text) + find('table tbody tr', text: text) + end + + def find_row_checkbox(text) + find_row(text).find('td:first-child input[type="checkbox"]') + end + + def select_row(text) + find_row_checkbox(text).check + end end end From 2830c385455772499ae133e54c70eb22b1eb52cb Mon Sep 17 00:00:00 2001 From: Elia Schito Date: Mon, 27 Nov 2023 13:50:17 +0100 Subject: [PATCH 3/3] Add a users index with scopes and batch deletion --- .../users/index/component.html.erb | 34 +++++++ .../solidus_admin/users/index/component.rb | 89 +++++++++++++++++++ .../solidus_admin/users/index/component.yml | 18 ++++ .../solidus_admin/users_controller.rb | 50 +++++++++++ admin/config/locales/users.en.yml | 6 ++ admin/config/routes.rb | 6 ++ admin/spec/features/users_spec.rb | 34 +++++++ 7 files changed, 237 insertions(+) create mode 100644 admin/app/components/solidus_admin/users/index/component.html.erb create mode 100644 admin/app/components/solidus_admin/users/index/component.rb create mode 100644 admin/app/components/solidus_admin/users/index/component.yml create mode 100644 admin/app/controllers/solidus_admin/users_controller.rb create mode 100644 admin/config/locales/users.en.yml create mode 100644 admin/spec/features/users_spec.rb diff --git a/admin/app/components/solidus_admin/users/index/component.html.erb b/admin/app/components/solidus_admin/users/index/component.html.erb new file mode 100644 index 00000000000..192dd4ce16a --- /dev/null +++ b/admin/app/components/solidus_admin/users/index/component.html.erb @@ -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_path, + next: next_page_path, + 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 %> diff --git a/admin/app/components/solidus_admin/users/index/component.rb b/admin/app/components/solidus_admin/users/index/component.rb new file mode 100644 index 00000000000..3965253e481 --- /dev/null +++ b/admin/app/components/solidus_admin/users/index/component.rb @@ -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_path + solidus_admin.url_for(**request.params, page: @page.number - 1, only_path: true) unless @page.first? + end + + def next_page_path + solidus_admin.url_for(**request.params, page: @page.next_param, only_path: true) unless @page.last? + 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 diff --git a/admin/app/components/solidus_admin/users/index/component.yml b/admin/app/components/solidus_admin/users/index/component.yml new file mode 100644 index 00000000000..179e1336f95 --- /dev/null +++ b/admin/app/components/solidus_admin/users/index/component.yml @@ -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 diff --git a/admin/app/controllers/solidus_admin/users_controller.rb b/admin/app/controllers/solidus_admin/users_controller.rb new file mode 100644 index 00000000000..90518339c07 --- /dev/null +++ b/admin/app/controllers/solidus_admin/users_controller.rb @@ -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 diff --git a/admin/config/locales/users.en.yml b/admin/config/locales/users.en.yml new file mode 100644 index 00000000000..c15e9fb0faa --- /dev/null +++ b/admin/config/locales/users.en.yml @@ -0,0 +1,6 @@ +en: + solidus_admin: + users: + title: "Users" + destroy: + success: "Users were successfully removed." diff --git a/admin/config/routes.rb b/admin/config/routes.rb index 587c5599468..5714b72cd08 100644 --- a/admin/config/routes.rb +++ b/admin/config/routes.rb @@ -30,4 +30,10 @@ get :customers_for end end + + resources :users, only: [:index] do + collection do + delete :destroy + end + end end diff --git a/admin/spec/features/users_spec.rb b/admin/spec/features/users_spec.rb new file mode 100644 index 00000000000..ceef0b54d75 --- /dev/null +++ b/admin/spec/features/users_spec.rb @@ -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