From c734f99c547f6eac7e88493aebe09df7625564d7 Mon Sep 17 00:00:00 2001 From: Madeline Collier Date: Fri, 13 Dec 2024 12:19:27 +0100 Subject: [PATCH] Add new admin store credits invalidate flow --- .../edit_validity/component.html.erb | 22 ++++++ .../store_credits/edit_validity/component.rb | 19 +++++ .../store_credits/edit_validity/component.yml | 5 ++ .../store_credits/show/component.html.erb | 6 +- .../users/store_credits/show/component.rb | 1 + .../solidus_admin/store_credits_controller.rb | 61 +++++++++++++-- admin/config/locales/store_credits.en.yml | 3 + admin/config/routes.rb | 2 + admin/spec/features/store_credits_spec.rb | 46 +++++++++--- .../solidus_admin/store_credits_spec.rb | 75 +++++++++++++++++++ 10 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 admin/app/components/solidus_admin/users/store_credits/edit_validity/component.html.erb create mode 100644 admin/app/components/solidus_admin/users/store_credits/edit_validity/component.rb create mode 100644 admin/app/components/solidus_admin/users/store_credits/edit_validity/component.yml diff --git a/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.html.erb b/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.html.erb new file mode 100644 index 0000000000..9ff928eddf --- /dev/null +++ b/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.html.erb @@ -0,0 +1,22 @@ +<%= turbo_frame_tag :edit_validity_modal do %> + <%= render component("ui/modal").new(title: t(".title")) do |modal| %> + <%= form_for @store_credit, url: solidus_admin.invalidate_user_store_credit_path(@user, @store_credit), method: :put, html: { id: form_id } do |f| %> +
+ <%= render component("ui/forms/field").select( + f, + :store_credit_reason_id, + store_credit_reasons_select_options.html_safe, + include_blank: t('spree.choose_reason'), + html: { required: true } + ) %> +
+ <% modal.with_actions do %> +
+ <%= render component("ui/button").new(scheme: :secondary, text: t('.cancel')) %> +
+ <%= render component("ui/button").new(form: form_id, scheme: :danger, type: :submit, text: t('.submit')) %> + <% end %> + <% end %> + <% end %> +<% end %> +<%= render component("users/store_credits/show").new(user: @user, store_credit: @store_credit, events: @store_credit_events) %> diff --git a/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.rb b/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.rb new file mode 100644 index 0000000000..9302d56fbd --- /dev/null +++ b/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class SolidusAdmin::Users::StoreCredits::EditValidity::Component < SolidusAdmin::BaseComponent + def initialize(user:, store_credit:, events:, reasons:) + @user = user + @store_credit = store_credit + @store_credit_events = events + @store_credit_reasons = reasons + end + + def form_id + dom_id(@store_credit, "#{stimulus_id}_edit_validity_form") + end + + def store_credit_reasons_select_options + # Placeholder + Store Credit Reasons + "" + options_from_collection_for_select(@store_credit_reasons, :id, :name) + end +end diff --git a/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.yml b/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.yml new file mode 100644 index 0000000000..2af0b913de --- /dev/null +++ b/admin/app/components/solidus_admin/users/store_credits/edit_validity/component.yml @@ -0,0 +1,5 @@ +en: + title: Invalidate Store Credit + cancel: Cancel + submit: Invalidate + choose_reason: Choose Reason For Invalidating diff --git a/admin/app/components/solidus_admin/users/store_credits/show/component.html.erb b/admin/app/components/solidus_admin/users/store_credits/show/component.html.erb index 7592c642dd..066414def5 100644 --- a/admin/app/components/solidus_admin/users/store_credits/show/component.html.erb +++ b/admin/app/components/solidus_admin/users/store_credits/show/component.html.erb @@ -29,16 +29,18 @@ <% if @store_credit.invalidateable? %> <%= render component("ui/button").new( scheme: :danger, - tag: :a, + "data-action": "click->#{stimulus_id}#actionButtonClicked", + "data-#{stimulus_id}-url-param": solidus_admin.edit_validity_user_store_credit_path(@user, @store_credit, _turbo_frame: :edit_validity_modal), text: t(".invalidate"), - href: spree.edit_validity_admin_user_store_credit_path(@user, @store_credit) )%> <% end %> + <%= render component("ui/button").new( "data-action": "click->#{stimulus_id}#actionButtonClicked", "data-#{stimulus_id}-url-param": solidus_admin.edit_memo_user_store_credit_path(@user, @store_credit, _turbo_frame: :edit_memo_modal), text: t(".edit_memo"), )%> + <% if @store_credit.editable? %> <%= render component("ui/button").new( "data-action": "click->#{stimulus_id}#actionButtonClicked", diff --git a/admin/app/components/solidus_admin/users/store_credits/show/component.rb b/admin/app/components/solidus_admin/users/store_credits/show/component.rb index 945979acb9..25ad16da70 100644 --- a/admin/app/components/solidus_admin/users/store_credits/show/component.rb +++ b/admin/app/components/solidus_admin/users/store_credits/show/component.rb @@ -52,6 +52,7 @@ def turbo_frames %w[ edit_amount_modal edit_memo_modal + edit_validity_modal ] end diff --git a/admin/app/controllers/solidus_admin/store_credits_controller.rb b/admin/app/controllers/solidus_admin/store_credits_controller.rb index 44f6ccd0cf..3f3dbc9e77 100644 --- a/admin/app/controllers/solidus_admin/store_credits_controller.rb +++ b/admin/app/controllers/solidus_admin/store_credits_controller.rb @@ -3,8 +3,8 @@ module SolidusAdmin class StoreCreditsController < SolidusAdmin::BaseController before_action :set_user - before_action :set_store_credit, only: [:show, :edit_amount, :update_amount, :edit_memo, :update_memo] - before_action :set_store_credit_reasons, only: [:edit_amount, :update_amount] + before_action :set_store_credit, only: [:show, :edit_amount, :update_amount, :edit_memo, :update_memo, :edit_validity, :invalidate] + before_action :set_store_credit_reasons, only: [:edit_amount, :update_amount, :edit_validity, :invalidate] def index @store_credits = Spree::StoreCredit.where(user_id: @user.id).order(id: :desc) @@ -54,7 +54,7 @@ def update_amount end end else - render_edit_amount_with_errors and return + render_edit_with_errors and return end end @@ -91,6 +91,45 @@ def update_memo end end + def edit_validity + @store_credit_events = @store_credit.store_credit_events.chronological + + respond_to do |format| + format.html { + render component("users/store_credits/edit_validity").new( + user: @user, + store_credit: @store_credit, + events: @store_credit_events, + reasons: @store_credit_reasons + ) + } + end + end + + def invalidate + return unless ensure_store_credit_reason + + if @store_credit.invalidate(@store_credit_reason, spree_current_user) + flash[:notice] = t('.success') + else + # Ensure store_credit_reason handles invalid param/form submissions and modal re-rendering. + # This is just a fallback error state in case anything goes wrong with StoreCredit#invalidate. + flash[:error] = t('.failure') + end + + respond_to do |format| + flash[:notice] = t('.success') + + format.html do + redirect_to solidus_admin.user_store_credit_path(@user, @store_credit), status: :see_other + end + + format.turbo_stream do + render turbo_stream: '' + end + end + end + private def set_store_credit @@ -107,17 +146,23 @@ def set_store_credit_reasons def permitted_store_credit_params permitted_params = [:amount, :currency, :category_id, :memo] - permitted_params << :store_credit_reason_id if action_name.to_sym == :update_amount + permitted_params << :store_credit_reason_id if [:update_amount, :invalidate].include?(action_name.to_sym) params.require(:store_credit).permit(permitted_params).merge(created_by: spree_current_user) end - def render_edit_amount_with_errors + def render_edit_with_errors @store_credit_events = @store_credit.store_credit_events.chronological + template = if action_name.to_sym == :invalidate + "edit_validity" + else + "edit_amount" + end + respond_to do |format| format.html do - render component("users/store_credits/edit_amount").new( + render component("users/store_credits/#{template}").new( user: @user, store_credit: @store_credit, events: @store_credit_events, @@ -131,7 +176,7 @@ def render_edit_amount_with_errors def ensure_amount if permitted_store_credit_params[:amount].blank? @store_credit.errors.add(:amount, :greater_than, count: 0, value: permitted_store_credit_params[:amount]) - render_edit_amount_with_errors + render_edit_with_errors return false end true @@ -142,7 +187,7 @@ def ensure_store_credit_reason if @store_credit_reason.blank? @store_credit.errors.add(:store_credit_reason_id, "Store Credit reason must be provided") - render_edit_amount_with_errors + render_edit_with_errors return false end true diff --git a/admin/config/locales/store_credits.en.yml b/admin/config/locales/store_credits.en.yml index 694c81bf1b..8f7f992d4e 100644 --- a/admin/config/locales/store_credits.en.yml +++ b/admin/config/locales/store_credits.en.yml @@ -11,3 +11,6 @@ en: update_memo: success: "Store credit was successfully updated." failure: "Something went wrong. Store credit could not be updated." + invalidate: + success: "Store credit was successfully invalidated." + failure: "Something went wrong. Store credit could not be invalidated." diff --git a/admin/config/routes.rb b/admin/config/routes.rb index 6f6708c14a..ffa0223158 100644 --- a/admin/config/routes.rb +++ b/admin/config/routes.rb @@ -59,6 +59,8 @@ put :update_amount get :edit_memo put :update_memo + get :edit_validity + put :invalidate end end end diff --git a/admin/spec/features/store_credits_spec.rb b/admin/spec/features/store_credits_spec.rb index 3b67f02d25..462e9d12c1 100644 --- a/admin/spec/features/store_credits_spec.rb +++ b/admin/spec/features/store_credits_spec.rb @@ -75,18 +75,6 @@ expect(page).to have_content("Added") end - it "allows invalidating of the store credit" do - click_on "Invalidate" - select "credit given in error", from: "store_credit_reason_id" - click_on "Invalidate" - expect(page).to have_content("Store Credit History") - expect(page).to have_content("Action") - expect(page).to have_content("Added") - expect(page).to have_content("Invalidated") - expect(page).to have_content("Reason for updating") - expect(page).to have_content("credit given in error") - end - context "when editing the store credit amount" do context "with invalid amount" do it "shows the appropriate error message" do @@ -137,6 +125,40 @@ end end + context "when invalidating" do + context "without a valid reason" do + it "shows the appropriate error message" do + click_on "Invalidate" + expect(page).to have_selector("dialog", wait: 5) + expect(page).to have_content("Invalidate Store Credit") + + within("dialog") do + click_on "Invalidate" + expect(page).to have_content("Store Credit reason must be provided") + click_on "Cancel" + end + end + end + + context "with a valid reason" do + it "invalidates the store credit" do + click_on "Invalidate" + expect(page).to have_selector("dialog", wait: 5) + expect(page).to have_content("Invalidate Store Credit") + + within("dialog") do + select "credit given in error", from: "store_credit[store_credit_reason_id]" + click_on "Invalidate" + end + + expect(page).to have_content("Store credit was successfully invalidated.") + expect(page).to have_content("Invalidated") + expect(page).to have_content("credit given in error") + expect(page).not_to have_content("Edit Amount") + end + end + end + context "when editing the store credit memo" do it "allows editing of the store credit memo" do click_on "Edit Memo" diff --git a/admin/spec/requests/solidus_admin/store_credits_spec.rb b/admin/spec/requests/solidus_admin/store_credits_spec.rb index e7ebc7f6ac..e691bde5c1 100644 --- a/admin/spec/requests/solidus_admin/store_credits_spec.rb +++ b/admin/spec/requests/solidus_admin/store_credits_spec.rb @@ -10,6 +10,7 @@ let(:valid_params) { { amount: 100, store_credit_reason_id: create(:store_credit_reason).id } } let(:invalid_params) { { amount: nil } } let(:valid_memo_params) { { memo: "Updated memo text" } } + let(:invalid_reason_params) { { store_credit_reason_id: nil } } before do allow_any_instance_of(SolidusAdmin::BaseController).to receive(:spree_current_user).and_return(admin_user) @@ -91,6 +92,80 @@ end end + describe "GET /edit_validity" do + it "renders the edit_validity template with a 200 OK status" do + get solidus_admin.edit_validity_user_store_credit_path(user, store_credit) + expect(response).to have_http_status(:ok) + expect(response.body).to include(store_credit.amount.to_s) + end + end + + describe "PUT /invalidate" do + context "with valid parameters" do + let(:store_credit_reason) { create(:store_credit_reason) } + + it "invalidates the store credit" do + expect { + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: { store_credit_reason_id: store_credit_reason.id } } + }.to change { store_credit.reload.invalidated? }.from(false).to(true) + end + + it "redirects to the store credit show page with a 303 See Other status" do + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: { store_credit_reason_id: store_credit_reason.id } } + expect(response).to redirect_to(solidus_admin.user_store_credit_path(user, store_credit)) + expect(response).to have_http_status(:see_other) + end + + it "displays a success flash message" do + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: { store_credit_reason_id: store_credit_reason.id } } + follow_redirect! + expect(response.body).to include("Store credit was successfully invalidated.") + end + end + + context "with invalid parameters" do + it "does not invalidate the store credit" do + expect { + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: invalid_reason_params } + }.not_to change { store_credit.reload.invalidated? } + end + + it "renders the edit_validity template with unprocessable_entity status" do + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: invalid_reason_params } + expect(response).to have_http_status(:unprocessable_entity) + end + + it "displays error messages in the response" do + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: invalid_reason_params } + expect(response.body).to include("Store Credit reason must be provided") + end + end + + context "when the database update fails" do + before do + allow_any_instance_of(Spree::StoreCredit).to receive(:invalidate).and_return(false) + end + + it "does not invalidate the store credit" do + expect { + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: valid_params } + }.not_to change { store_credit.reload.invalidated? } + end + + it "redirects to the store credit show page with a 303 See Other status" do + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: valid_params } + expect(response).to redirect_to(solidus_admin.user_store_credit_path(user, store_credit)) + expect(response).to have_http_status(:see_other) + end + + it "displays a failure flash message" do + put solidus_admin.invalidate_user_store_credit_path(user, store_credit), params: { store_credit: valid_params } + follow_redirect! + expect(response.body).to include("Something went wrong. Store credit could not be invalidated.") + end + end + end + describe "GET /edit_memo" do it "renders the edit_memo template with a 200 OK status" do get solidus_admin.edit_memo_user_store_credit_path(user, store_credit)