diff --git a/admin/app/components/solidus_admin/users/store_credits/index/component.html.erb b/admin/app/components/solidus_admin/users/store_credits/index/component.html.erb new file mode 100644 index 0000000000..5d70c4be2b --- /dev/null +++ b/admin/app/components/solidus_admin/users/store_credits/index/component.html.erb @@ -0,0 +1,49 @@ +<%= page do %> + <%= page_header do %> + <%= page_header_back(solidus_admin.users_path) %> + <%= page_header_title(t(".title", email: @user.email)) %> + + <%= page_header_actions do %> + <%= render component("ui/button").new(tag: :a, text: t(".add_store_credit"), href: spree.new_admin_user_store_credit_url(user_id: @user.id, only_path: true)) %> + <% end %> + <% end %> + + <%= page_header do %> + <% tabs.each do |tab| %> + <%= render(component("ui/button").new(tag: :a, scheme: :ghost, text: tab[:text], 'aria-current': tab[:current], href: tab[:href])) %> + <% end %> + <% end %> + + <%= page_with_sidebar do %> + <%= page_with_sidebar_main do %> + + <% if @store_credits.present? %> + <% @store_credits.group_by(&:currency).each do |currency, credits| %> + <% title = [t('spree.admin.store_credits.current_balance'), Spree::Money.new(credits.sum(&:amount_remaining), currency: currency)].join(" ") %> + + <%= render component('ui/panel').new(title: title) do %> + <%= render component('ui/table').new( + id: stimulus_id, + data: { + class: model_class, + rows: credits, + fade: -> (_order) { false }, + columns: columns, + url: -> { row_url(_1) }, + }, + )%> + <% end %> + <% end %> + <% else %> + <%= render component('ui/panel').new(title: t(".store_credit")) do %> + <%= t(".no_credits_found") %> + <%= render component("ui/button").new(tag: :a, text: t(".create_one"), href: spree.new_admin_user_store_credit_url(user_id: @user.id, only_path: true)) %> + <% end %> + <% end %> + <% end %> + + <%= page_with_sidebar_aside do %> + <%= render component("users/stats").new(user: @user) %> + <% end %> + <% end %> +<% end %> diff --git a/admin/app/components/solidus_admin/users/store_credits/index/component.rb b/admin/app/components/solidus_admin/users/store_credits/index/component.rb new file mode 100644 index 0000000000..e039fd8cc6 --- /dev/null +++ b/admin/app/components/solidus_admin/users/store_credits/index/component.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +class SolidusAdmin::Users::StoreCredits::Index::Component < SolidusAdmin::BaseComponent + include SolidusAdmin::Layout::PageHelpers + + def initialize(user:, store_credits:) + @user = user + @store_credits = store_credits + end + + def model_class + Spree::StoreCredit + end + + def tabs + [ + { + text: t('.account'), + href: solidus_admin.user_path(@user), + current: false, + }, + { + text: t('.addresses'), + href: solidus_admin.addresses_user_path(@user), + current: false, + }, + { + text: t('.order_history'), + href: solidus_admin.orders_user_path(@user), + current: false, + }, + { + text: t('.items'), + href: spree.items_admin_user_path(@user), + current: false, + }, + { + text: t('.store_credit'), + href: solidus_admin.store_credits_user_path(@user), + current: true, + }, + ] + end + + def rows + @store_credits + end + + def row_url(store_credit) + spree.admin_user_store_credit_path(@user, store_credit) + end + + def columns + [ + { + header: :credited, + col: { class: "w-[12%]" }, + data: ->(store_credit) do + content_tag :div, store_credit.display_amount.to_html, class: "text-sm" + end + }, + { + header: :authorized, + col: { class: "w-[13%]" }, + data: ->(store_credit) do + content_tag :div, store_credit.display_amount_authorized.to_html, class: "text-sm" + end + }, + { + header: :used, + col: { class: "w-[9%]" }, + data: ->(store_credit) do + content_tag :div, store_credit.display_amount_used.to_html, class: "text-sm" + end + }, + { + header: :type, + col: { class: "w-[13%]" }, + data: ->(store_credit) do + component('ui/badge').new(name: store_credit.credit_type.name, color: :blue) + end + }, + { + header: :created_by, + col: { class: "w-[22%]" }, + data: ->(store_credit) do + content_tag :div, store_credit.created_by_email, class: "font-semibold text-sm" + end + }, + { + header: :issued_on, + col: { class: "w-[16%]" }, + data: ->(store_credit) do + I18n.l(store_credit.created_at.to_date) + end + }, + { + header: :invalidated, + col: { class: "w-[15%]" }, + data: ->(store_credit) do + store_credit.invalidated? ? component('ui/badge').yes : component('ui/badge').no + end + }, + ] + end +end diff --git a/admin/app/components/solidus_admin/users/store_credits/index/component.yml b/admin/app/components/solidus_admin/users/store_credits/index/component.yml new file mode 100644 index 0000000000..6fc7fa06f3 --- /dev/null +++ b/admin/app/components/solidus_admin/users/store_credits/index/component.yml @@ -0,0 +1,12 @@ +en: + title: "Users / %{email} / Store Credit" + account: Account + addresses: Addresses + order_history: Order History + items: Items + store_credit: Store Credit + last_active: Last Active + add_store_credit: Add Store Credit + no_credits_found: No Store Credits found. + create_one: Create One + back: Back diff --git a/admin/app/controllers/solidus_admin/users_controller.rb b/admin/app/controllers/solidus_admin/users_controller.rb index 7e7d181a1b..2162645945 100644 --- a/admin/app/controllers/solidus_admin/users_controller.rb +++ b/admin/app/controllers/solidus_admin/users_controller.rb @@ -5,7 +5,7 @@ class UsersController < SolidusAdmin::BaseController include SolidusAdmin::ControllerHelpers::Search include Spree::Core::ControllerHelpers::StrongParameters - before_action :set_user, only: [:edit, :addresses, :update_addresses, :orders, :items] + before_action :set_user, only: [:edit, :addresses, :update_addresses, :orders, :items, :store_credits] search_scope(:all, default: true) search_scope(:customers) { _1.left_outer_joins(:role_users).where(role_users: { id: nil }) } @@ -81,6 +81,14 @@ def destroy redirect_back_or_to users_path, status: :see_other end + def store_credits + @store_credits = Spree::StoreCredit.where(user_id: @user.id).order(id: :desc) + + respond_to do |format| + format.html { render component("users/store_credits/index").new(user: @user, store_credits: @store_credits) } + end + end + private def set_user diff --git a/admin/config/routes.rb b/admin/config/routes.rb index 862ee70212..c42101b772 100644 --- a/admin/config/routes.rb +++ b/admin/config/routes.rb @@ -51,6 +51,7 @@ put :update_addresses get :orders get :items + get :store_credits end end diff --git a/admin/spec/features/users_spec.rb b/admin/spec/features/users_spec.rb index b6ac4e097a..b19e31b1e9 100644 --- a/admin/spec/features/users_spec.rb +++ b/admin/spec/features/users_spec.rb @@ -252,4 +252,57 @@ end end end + + context "when viewing a user's store credits" do + context "when a user has no store credits" do + before do + create(:user, email: "customer@example.com") + visit "/admin/users" + find_row("customer@example.com").click + click_on "Store Credit" + end + + it "shows the store credits page" do + expect(page).to have_content("Users / customer@example.com / Store Credit") + expect(page).to have_content("Lifetime Stats") + expect(page).to have_content("Store Credit") + expect(page).to be_axe_clean + end + + it "shows the appropriate content" do + expect(page).to have_content("No Store Credits found.") + end + end + + context "when a user has store credits" do + let!(:store_credit) { create(:store_credit, amount: 199.00, currency: "USD") } + + before do + store_credit.user.update(email: "customer@example.com") + + visit "/admin/users" + find_row("customer@example.com").click + click_on "Store Credit" + end + + it "shows the store credits page" do + expect(page).to have_content("Users / customer@example.com / Store Credit") + expect(page).to have_content("Lifetime Stats") + expect(page).to have_content("Store Credit") + expect(page).to be_axe_clean + end + + it "lists the user's store credit" do + expect(page).to have_content("Current balance: $199.00") + expect(page).to have_content("Credited") + expect(page).to have_content("Authorized") + expect(page).to have_content("Used") + expect(page).to have_content("Type") + expect(page).to have_content("Created by") + expect(page).to have_content("Issued on") + expect(page).to have_content("Invalidated") + expect(page).not_to have_content("No Store Credits found.") + end + end + end end diff --git a/admin/spec/requests/solidus_admin/users_spec.rb b/admin/spec/requests/solidus_admin/users_spec.rb index c2ca7c88e2..21fcaa99b8 100644 --- a/admin/spec/requests/solidus_admin/users_spec.rb +++ b/admin/spec/requests/solidus_admin/users_spec.rb @@ -99,6 +99,13 @@ end end + describe "GET /store_credits" do + it "renders the store credits template with a 200 OK status" do + get solidus_admin.store_credits_user_path(user) + expect(response).to have_http_status(:ok) + end + end + describe "GET /addresses" do it "renders the addresses template with a 200 OK status" do get solidus_admin.addresses_user_path(user)