Skip to content

Commit

Permalink
Merge pull request solidusio#6036 from MadelineCollier/admin-user-sto…
Browse files Browse the repository at this point in the history
…re-credit-create

[Admin][Users] Add new admin store credits create flow
  • Loading branch information
MadelineCollier authored Dec 18, 2024
2 parents 811f14d + 0517f29 commit 89730ee
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
<%= 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)) %>
<%= render component("ui/button").new(
"data-action": "click->#{stimulus_id}#actionButtonClicked",
"data-#{stimulus_id}-url-param": solidus_admin.new_user_store_credit_path(user_id: @user.id, _turbo_frame: :new_store_credit_modal),
text: t(".add_store_credit"),
)%>
<% end %>
<% end %>

Expand Down Expand Up @@ -37,7 +41,11 @@
<% 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)) %>
<%= render component("ui/button").new(
"data-action": "click->#{stimulus_id}#actionButtonClicked",
"data-#{stimulus_id}-url-param": solidus_admin.new_user_store_credit_path(user_id: @user.id, _turbo_frame: :new_store_credit_modal),
text: t(".create_one"),
)%>
<% end %>
<% end %>
<% end %>
Expand All @@ -46,4 +54,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 @@ -42,6 +42,12 @@ def tabs
]
end

def turbo_frames
%w[
new_store_credit_modal
]
end

def rows
@store_credits
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<%= turbo_frame_tag :new_store_credit_modal do %>
<%= render component("ui/modal").new(title: t(".title")) do |modal| %>
<%= form_for @store_credit, url: solidus_admin.user_store_credits_path(@user), method: :post, 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,
:currency,
currency_select_options.html_safe,
include_blank: t("spree.currency"),
html: { required: true }
) %>
<%= render component("ui/forms/field").select(
f,
:category_id,
store_credit_categories_select_options.html_safe,
include_blank: t("spree.category"),
html: { required: true }
) %>
<%= render component("ui/forms/field").text_field(f, :memo) %>
</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/index").new(user: @user, store_credits: @store_credits) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

class SolidusAdmin::Users::StoreCredits::New::Component < SolidusAdmin::BaseComponent
def initialize(user:, store_credit:, categories:)
@user = user
@store_credit = store_credit
@store_credit_categories = categories
@store_credits = Spree::StoreCredit.where(user_id: @user.id).order(id: :desc)
end

def form_id
dom_id(@store_credit, "#{stimulus_id}_new_form")
end

def currency_select_options
options_from_collection_for_select(Spree::Config.available_currencies, :iso_code, :iso_code, Spree::Config.currency)
end

def store_credit_categories_select_options
# Placeholder + Store Credit Categories
"<option value>#{t('.choose_category')}</option>" + options_from_collection_for_select(@store_credit_categories, :id, :name)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
en:
title: New Store Credit
cancel: Cancel
submit: Create
choose_category: Choose Store Credit Category
103 changes: 86 additions & 17 deletions admin/app/controllers/solidus_admin/store_credits_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class StoreCreditsController < SolidusAdmin::BaseController
before_action :set_user
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]
before_action :set_store_credit_events, only: [:show, :edit_amount, :edit_memo, :edit_validity]
before_action :set_store_credit_categories, only: [:new]

def index
@store_credits = Spree::StoreCredit.where(user_id: @user.id).order(id: :desc)
Expand All @@ -15,16 +17,54 @@ def index
end

def show
@store_credit_events = @store_credit.store_credit_events.chronological

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

def edit_amount
@store_credit_events = @store_credit.store_credit_events.chronological
def new
@store_credit ||= Spree::StoreCredit.new

respond_to do |format|
format.html {
render component("users/store_credits/new").new(
user: @user,
store_credit: @store_credit,
categories: @store_credit_categories
)
}
end
end

def create
@store_credit = @user.store_credits.build(
permitted_store_credit_params.merge({
created_by: spree_current_user,
action_originator: spree_current_user
})
)

return unless ensure_amount { render_new_with_errors }
return unless ensure_store_credit_category { render_new_with_errors }

if @store_credit.save
respond_to do |format|
flash[:notice] = t('.success')

format.html do
redirect_to solidus_admin.user_store_credits_path(@user), status: :see_other
end

format.turbo_stream do
render turbo_stream: '<turbo-stream action="refresh" />'
end
end
else
render_new_with_errors
end
end

def edit_amount
respond_to do |format|
format.html {
render component("users/store_credits/edit_amount").new(
Expand All @@ -38,8 +78,8 @@ def edit_amount
end

def update_amount
return unless ensure_amount
return unless ensure_store_credit_reason
return unless ensure_amount { render_edit_with_errors }
return unless ensure_store_credit_reason { render_edit_with_errors }

if @store_credit.update_amount(permitted_store_credit_params[:amount], @store_credit_reason, spree_current_user)
respond_to do |format|
Expand All @@ -54,13 +94,11 @@ def update_amount
end
end
else
render_edit_with_errors and return
render_edit_with_errors
end
end

def edit_memo
@store_credit_events = @store_credit.store_credit_events.chronological

respond_to do |format|
format.html {
render component("users/store_credits/edit_memo").new(
Expand Down Expand Up @@ -92,8 +130,6 @@ def update_memo
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(
Expand All @@ -107,7 +143,7 @@ def edit_validity
end

def invalidate
return unless ensure_store_credit_reason
return unless ensure_store_credit_reason { render_edit_with_errors }

if @store_credit.invalidate(@store_credit_reason, spree_current_user)
flash[:notice] = t('.success')
Expand All @@ -118,8 +154,6 @@ def invalidate
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
Expand All @@ -144,15 +178,39 @@ def set_store_credit_reasons
@store_credit_reasons = Spree::StoreCreditReason.active.order(:name)
end

def set_store_credit_categories
@store_credit_categories = Spree::StoreCreditCategory.all.order(:name)
end

def set_store_credit_events
@store_credit_events = @store_credit.store_credit_events.chronological
end

def permitted_store_credit_params
permitted_params = [:amount, :currency, :category_id, :memo]
permitted_params << :category_id if action_name.to_sym == :create
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_new_with_errors
set_store_credit_categories

respond_to do |format|
format.html do
render component("users/store_credits/new").new(
user: @user,
store_credit: @store_credit,
categories: @store_credit_categories
),
status: :unprocessable_entity
end
end
end

def render_edit_with_errors
@store_credit_events = @store_credit.store_credit_events.chronological
set_store_credit_events

template = if action_name.to_sym == :invalidate
"edit_validity"
Expand All @@ -176,7 +234,7 @@ def render_edit_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_with_errors
yield if block_given? # Block is for error template rendering on a per-action basis so this can be re-used.
return false
end
true
Expand All @@ -187,7 +245,18 @@ 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_with_errors
yield if block_given? # Block is for error template rendering on a per-action basis so this can be re-used.
return false
end
true
end

def ensure_store_credit_category
@store_credit_category = Spree::StoreCreditCategory.find_by(id: permitted_store_credit_params[:category_id])

if @store_credit_category.blank?
@store_credit.errors.add(:category_id, "Store Credit category must be provided")
yield if block_given? # Block is for error template rendering on a per-action basis so this can be re-used.
return false
end
true
Expand Down
2 changes: 1 addition & 1 deletion admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
get :items
end

resources :store_credits, only: [:index, :show], constraints: { id: /\d+/ }, controller: "store_credits" do
resources :store_credits, only: [:index, :show, :new, :create], controller: "store_credits" do
member do
get :edit_amount
put :update_amount
Expand Down
36 changes: 33 additions & 3 deletions admin/spec/features/store_credits_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

describe "StoreCredits", :js, type: :feature do
let(:admin) { create(:admin_user, email: "admin@example.com") }
let!(:store_credit_reason) { create(:store_credit_reason, name: "credit given in error") }
let!(:store_credit_category) { create(:store_credit_category, name: "Gift Card") }
let!(:store_credit_type) { create(:primary_credit_type) }

before do
sign_in admin
Expand All @@ -27,11 +30,40 @@
it "shows the appropriate content" do
expect(page).to have_content("No Store Credits found.")
end

it "allows creation of a new store credit" do
click_on "Create One"

expect(page).to have_selector("dialog", wait: 5)
expect(page).to have_content("New Store Credit")

within("dialog") do
fill_in "Amount", with: ""
select "Gift Card", from: "store_credit[category_id]"
click_on "Create"
expect(page).to have_content("must be greater than 0")
click_on "Cancel"
end

click_on "Create One"

expect(page).to have_selector("dialog", wait: 5)
expect(page).to have_content("New Store Credit")

within("dialog") do
fill_in "Amount", with: "666.66"
select "Gift Card", from: "store_credit[category_id]"
fill_in "Memo", with: "A brand new store credit, how nice!"
click_on "Create"
end

expect(page).to have_content("Store credit was successfully created.")
expect(page).to have_content("Current balance: $666.66")
end
end

context "when a user has store credits" do
let!(:store_credit) { create(:store_credit, amount: 199.00, currency: "USD") }
let!(:store_credit_reason) { create(:store_credit_reason, name: "credit given in error") }

before do
store_credit.user.update(email: "customer@example.com")
Expand Down Expand Up @@ -61,8 +93,6 @@
end

context "when clicking through to a single store credit" do
let!(:store_credit_reason) { create(:store_credit_reason, name: "credit given in error") }

before do
stub_authorization!(admin)
find_row("$199.00").click
Expand Down
Loading

0 comments on commit 89730ee

Please sign in to comment.