Skip to content

Commit

Permalink
Merge pull request #6031 from MadelineCollier/admin-user-store-credit…
Browse files Browse the repository at this point in the history
…-refactor

[Admin][Users] Add new admin store credits edit_amount flow
  • Loading branch information
MadelineCollier authored Dec 12, 2024
2 parents 6aa6b22 + 2eefd30 commit dd5bbc4
Show file tree
Hide file tree
Showing 14 changed files with 333 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def self.select(form, method, choices, object: nil, hint: nil, tip: nil, size: :
tag: :select,
choices:,
size:,
value: object.public_send(method),
value: (object.public_send(method) if object.respond_to?(method)),
error: (errors.to_sentence.capitalize if errors),
**attributes,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<%= turbo_frame_tag :edit_amount_modal do %>
<%= render component("ui/modal").new(title: t(".title")) do |modal| %>
<%= form_for @store_credit, url: solidus_admin.update_amount_user_store_credit_path(@user, @store_credit), method: :put, html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :amount, class: "required") %>
<%= 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 }
) %>
</div>
<% modal.with_actions do %>
<form method="dialog">
<%= render component("ui/button").new(scheme: :secondary, text: t('.cancel')) %>
</form>
<%= render component("ui/button").new(form: form_id, 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) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class SolidusAdmin::Users::StoreCredits::EditAmount::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_amount_form")
end

def store_credit_reasons_select_options
# Placeholder + Store Credit Reasons
"<option value>#{t('.choose_reason')}</option>" + options_from_collection_for_select(@store_credit_reasons, :id, :name)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
en:
title: Edit Store Credit
cancel: Cancel
submit: Update Store Credit
choose_reason: Choose Reason For Changing Amount
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def rows
end

def row_url(store_credit)
spree.admin_user_store_credit_path(@user, store_credit)
solidus_admin.user_store_credit_path(@user, store_credit)
end

def columns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@
<%= page_header do %>
<%= page_header_back(solidus_admin.users_path) %>
<%= page_header_title(t(".title", email: @user.email, amount: @store_credit.display_amount)) %>

<%= page_header_actions do %>
<%# TODO: can? actions in admin %>
<%# if @store_credit.editable? && can?(:edit, @store_credit) %>
<% if @store_credit.editable? %>
<%= render component("ui/button").new(tag: :a, text: t(".change_amount"), href: spree.edit_amount_admin_user_store_credit_path(@user, @store_credit)) %>
<% end %>
<%# TODO: can? actions in admin %>
<%# if @store_credit.invalidateable? && can?(:invalidate, @store_credit) %>
<% if @store_credit.invalidateable? %>
<%= render component("ui/button").new(scheme: :danger, tag: :a, text: t(".invalidate"), href: spree.edit_validity_admin_user_store_credit_path(@user, @store_credit)) %>
<% end %>

<% end %>
<% end %>

<%= page_header do %>
Expand All @@ -26,17 +12,40 @@

<%= page_with_sidebar do %>
<%= page_with_sidebar_main do %>
<%= render component('ui/panel').new(title: t(".store_credit")) do %>
<%= render component('ui/details_list').new(
items: [
{ label: t('.credited'), value: @store_credit.display_amount },
{ label: t('.created_by'), value: @store_credit.created_by_email },
{ label: t('.type'), value: @store_credit.category_name },
{ label: t('.memo'), value: @store_credit.memo }
]
) %>
<% end %>
<%= render component('ui/panel').new(title: t(".store_credit")) do |panel| %>
<% panel.with_section do %>
<%= render component('ui/details_list').new(
items: [
{ label: t('.credited'), value: @store_credit.display_amount },
{ label: t('.created_by'), value: @store_credit.created_by_email },
{ label: t('.type'), value: @store_credit.category_name },
{ label: t('.memo'), value: @store_credit.memo }
]
) %>
<% end %>

<% if @store_credit.editable? || @store_credit.invalidateable? %>
<% panel.with_section do %>
<div class="w-[100%] text-right">
<% if @store_credit.invalidateable? %>
<%= render component("ui/button").new(
scheme: :danger,
tag: :a,
text: t(".invalidate"),
href: spree.edit_validity_admin_user_store_credit_path(@user, @store_credit)
)%>
<% end %>
<% if @store_credit.editable? %>
<%= render component("ui/button").new(
"data-action": "click->#{stimulus_id}#actionButtonClicked",
"data-#{stimulus_id}-url-param": solidus_admin.edit_amount_user_store_credit_path(@user, @store_credit, _turbo_frame: :edit_amount_modal),
text: t(".edit"),
)%>
<% end %>
</div>
<% end %>
<% end %>
<% end %>

<% if @events.present? %>
<h1 class="font-semibold text-base text-center w-[100%]"><%= t(".store_credit_history") %></h1>
Expand All @@ -56,4 +65,8 @@
<%= render component("users/stats").new(user: @user) %>
<% end %>
<% end %>

<% turbo_frames.each do |frame| %>
<%= turbo_frame_tag frame %>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
actionButtonClicked(event) {
const url = new URL(event.params.url, "http://dummy.com")
const params = new URLSearchParams(url.search)
const frameId = params.get('_turbo_frame')
const frame = frameId ? { frame: frameId } : {}
// remove the custom _turbo_frame param from url search:
params.delete('_turbo_frame')
url.search = params.toString()

window.Turbo.visit(url.pathname + url.search, frame)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ def tabs
]
end

def turbo_frames
%w[
edit_amount_modal
]
end

def form_id
@form_id ||= "#{stimulus_id}--form-#{@store_credit.id}"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ en:
store_credit: Store Credit
last_active: Last Active
add_store_credit: Add Store Credit
change_amount: Change Amount
edit: Edit Amount
invalidate: Invalidate
store_credit_history: Store Credit History
credited: Credited
Expand Down
86 changes: 85 additions & 1 deletion admin/app/controllers/solidus_admin/store_credits_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
module SolidusAdmin
class StoreCreditsController < SolidusAdmin::BaseController
before_action :set_user
before_action :set_store_credit, only: [:show]
before_action :set_store_credit, only: [:show, :edit_amount, :update_amount]
before_action :set_store_credit_reasons, only: [:edit_amount, :update_amount]

def index
@store_credits = Spree::StoreCredit.where(user_id: @user.id).order(id: :desc)
Expand All @@ -21,6 +22,42 @@ def show
end
end

def edit_amount
@store_credit_events = @store_credit.store_credit_events.chronological

respond_to do |format|
format.html {
render component("users/store_credits/edit_amount").new(
user: @user,
store_credit: @store_credit,
events: @store_credit_events,
reasons: @store_credit_reasons
)
}
end
end

def update_amount
return unless ensure_amount
return unless ensure_store_credit_reason

if @store_credit.update_amount(permitted_store_credit_params[:amount], @store_credit_reason, spree_current_user)
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: '<turbo-stream action="refresh" />'
end
end
else
render_edit_page_with_errors and return
end
end

private

def set_store_credit
Expand All @@ -30,5 +67,52 @@ def set_store_credit
def set_user
@user = Spree.user_class.find(params[:user_id])
end

def set_store_credit_reasons
@store_credit_reasons = Spree::StoreCreditReason.active.order(:name)
end

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

params.require(:store_credit).permit(permitted_params).merge(created_by: spree_current_user)
end

def render_edit_page_with_errors
@store_credit_events = @store_credit.store_credit_events.chronological

respond_to do |format|
format.html do
render component("users/store_credits/edit_amount").new(
user: @user,
store_credit: @store_credit,
events: @store_credit_events,
reasons: @store_credit_reasons
),
status: :unprocessable_entity
end
end
end

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_page_with_errors
return false
end
true
end

def ensure_store_credit_reason
@store_credit_reason = Spree::StoreCreditReason.find_by(id: permitted_store_credit_params[:store_credit_reason_id])

if @store_credit_reason.blank?
@store_credit.errors.add(:store_credit_reason_id, "Store Credit reason must be provided")
render_edit_page_with_errors
return false
end
true
end
end
end
10 changes: 10 additions & 0 deletions admin/config/locales/store_credits.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
en:
solidus_admin:
store_credits:
title: "Store Credits"
destroy:
success: "Store credit was successfully removed."
create:
success: "Store credit was successfully created."
update_amount:
success: "Store credit was successfully updated."
7 changes: 6 additions & 1 deletion admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@
get :items
end

resources :store_credits, only: [:index, :show], controller: "store_credits"
resources :store_credits, only: [:index, :show], constraints: { id: /\d+/ }, controller: "store_credits" do
member do
get :edit_amount
put :update_amount
end
end
end

admin_resources :promotions, only: [:index, :destroy]
Expand Down
56 changes: 55 additions & 1 deletion admin/spec/features/users_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -310,14 +310,17 @@

before do
stub_authorization!(admin)
find_row("$199.00").click
end

it "shows individual store credit details" do
find_row("$199.00").click
expect(page).to have_content("Users / [email protected] / Store Credit / $199.00")
expect(page).to have_content("Store Credit History")
expect(page).to have_content("Action")
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"
Expand All @@ -328,6 +331,57 @@
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
click_on "Edit Amount"
expect(page).to have_selector("dialog", wait: 5)
expect(page).to have_content("Edit Store Credit")

within("dialog") do
fill_in "Amount", with: ""
click_on "Update Store Credit"
expect(page).to have_content("must be greater than 0")
click_on "Cancel"
end
end
end

context "without a valid reason" do
it "shows the appropriate error message" do
click_on "Edit Amount"
expect(page).to have_selector("dialog", wait: 5)
expect(page).to have_content("Edit Store Credit")

within("dialog") do
fill_in "Amount", with: "100"
click_on "Update Store Credit"
expect(page).to have_content("Store Credit reason must be provided")
click_on "Cancel"
end
end
end

context "with valid params" do
it "allows editing of the store credit amount" do
click_on "Edit Amount"
expect(page).to have_selector("dialog", wait: 5)
expect(page).to have_content("Edit Store Credit")

# Invalid amount
within("dialog") do
fill_in "Amount", with: "666"
select "credit given in error", from: "store_credit[store_credit_reason_id]"
click_on "Update Store Credit"
end

expect(page).to have_content("Users / [email protected] / Store Credit / $666.00")
expect(page).to have_content("Adjustment")
expect(page).to have_content("credit given in error")
end
end
end
end
end
end
Expand Down
Loading

0 comments on commit dd5bbc4

Please sign in to comment.