Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Admin] Add new users admin addresses page #5865

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<%= 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(".create_order_for_user"), href: spree.new_admin_order_path(user_id: @user.id)) %>
<% 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 %>
<%= render component('ui/panel').new(title: t(".billing_address")) do %>
<%= form_for @user, url: solidus_admin.update_addresses_user_path(@user), method: :put, html: { id: "#{form_id}_billing", autocomplete: "off", class: "bill_address" } do |f| %>

<%= render component('ui/forms/address').new(address: @bill_address, name: "user[bill_address_attributes]") %>
<div class="py-1.5 text-center">
<%= render component("ui/button").new(tag: :button, text: t(".update"), form: "#{form_id}_billing") %>
<%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.addresses_user_path(@user), scheme: :secondary) %>
</div>
<% end %>
<% end %>

<%= render component('ui/panel').new(title: t(".shipping_address")) do %>
<%= form_for @user, url: solidus_admin.update_addresses_user_path(@user), method: :put, html: { id: "#{form_id}_shipping", autocomplete: "off", class: "ship_address" } do |f| %>

<%= render component('ui/forms/address').new(address: @ship_address, name: "user[ship_address_attributes]") %>
<div class="py-1.5 text-center">
<%= render component("ui/button").new(tag: :button, text: t(".update"), form: "#{form_id}_shipping") %>
<%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.addresses_user_path(@user), scheme: :secondary) %>
</div>
<% end %>
<% end %>
<% end %>

<%= page_with_sidebar_aside do %>
<%= render component("ui/panel").new(title: t("spree.lifetime_stats")) do %>
<%= render component("ui/details_list").new(
items: [
{ label: t("spree.total_sales"), value: @user.display_lifetime_value.to_html },
{ label: t("spree.order_count"), value: @user.order_count.to_i },
{ label: t("spree.average_order_value"), value: @user.display_average_order_value.to_html },
{ label: t("spree.member_since"), value: @user.created_at.to_date },
{ label: t(".last_active"), value: last_login(@user) },
]
) %>
<% end %>
<% end %>
<% end %>
<% end %>
72 changes: 72 additions & 0 deletions admin/app/components/solidus_admin/users/addresses/component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

class SolidusAdmin::Users::Addresses::Component < SolidusAdmin::BaseComponent
include SolidusAdmin::Layout::PageHelpers

def initialize(user:, address: nil, type: nil)
@user = user
@bill_address = bill_address(address, type)
@ship_address = ship_address(address, type)
end

def form_id
@form_id ||= "#{stimulus_id}--form-#{@user.id}"
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: true,
},
{
text: t('.order_history'),
href: spree.orders_admin_user_path(@user),
current: false,
},
{
text: t('.items'),
href: spree.items_admin_user_path(@user),
current: false,
},
{
text: t('.store_credit'),
href: spree.admin_user_store_credits_path(@user),
current: false,
},
]
end

def last_login(user)
return t('.last_login.never') if user.try(:last_sign_in_at).blank?

t(

Check warning on line 49 in admin/app/components/solidus_admin/users/addresses/component.rb

View check run for this annotation

Codecov / codecov/patch

admin/app/components/solidus_admin/users/addresses/component.rb#L49

Added line #L49 was not covered by tests
'.last_login.login_time_ago',
# @note The second `.try` is here for the specs and for setups that use a
# custom User class which may not have this attribute.
last_login_time: time_ago_in_words(user.try(:last_sign_in_at))
).capitalize
end

def bill_address(address, type)
if address.present? && type == "bill"
address
else
@user.bill_address || Spree::Address.build_default
end
end

def ship_address(address, type)
if address.present? && type == "ship"
address
else
@user.ship_address || Spree::Address.build_default
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
en:
title: "Users / %{email} / Addresses"
account: Account
addresses: Addresses
order_history: Order History
items: Items
store_credit: Store Credit
last_active: Last Active
last_login:
login_time_ago: "%{last_login_time} ago"
never: Never
invitation_sent: Invitation sent
create_order_for_user: Create order for this user
update: Update
cancel: Cancel
back: Back
billing_address: Billing Address
shipping_address: Shipping Address
15 changes: 7 additions & 8 deletions admin/app/components/solidus_admin/users/edit/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,31 @@ def tabs
[
{
text: t('.account'),
href: solidus_admin.users_path,
current: action_name == "show",
href: solidus_admin.user_path(@user),
current: action_name == "edit",
},
{
text: t('.addresses'),
href: spree.addresses_admin_user_path(@user),
# @todo: update this "current" logic once folded into new admin
current: action_name != "show",
href: solidus_admin.addresses_user_path(@user),
current: action_name == "addresses",
},
{
text: t('.order_history'),
href: spree.orders_admin_user_path(@user),
# @todo: update this "current" logic once folded into new admin
current: action_name != "show",
current: action_name != "edit",
},
{
text: t('.items'),
href: spree.items_admin_user_path(@user),
# @todo: update this "current" logic once folded into new admin
current: action_name != "show",
current: action_name != "edit",
},
{
text: t('.store_credit'),
href: spree.admin_user_store_credits_path(@user),
# @todo: update this "current" logic once folded into new admin
current: action_name != "show",
current: action_name != "edit",
},
]
end
Expand Down
49 changes: 46 additions & 3 deletions admin/app/controllers/solidus_admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
module SolidusAdmin
class UsersController < SolidusAdmin::BaseController
include SolidusAdmin::ControllerHelpers::Search
include Spree::Core::ControllerHelpers::StrongParameters

before_action :set_user, only: [:edit, :addresses, :update_addresses]

search_scope(:all, default: true)
search_scope(:customers) { _1.left_outer_joins(:role_users).where(role_users: { id: nil }) }
Expand All @@ -23,9 +26,31 @@ def index
end
end

def edit
set_user
def addresses
respond_to do |format|
format.turbo_stream { render turbo_stream: '<turbo-stream action="refresh" />' }
format.html { render component('users/addresses').new(user: @user) }
end
end

def update_addresses
set_address_from_params

if @address.valid? && @user.update(user_params)
flash[:success] = t(".#{@type}.success")

respond_to do |format|
format.turbo_stream { render turbo_stream: '<turbo-stream action="refresh" />' }
format.html { render component('users/addresses').new(user: @user) }
end
else
respond_to do |format|
format.html { render component('users/addresses').new(user: @user, address: @address, type: @type), status: :unprocessable_entity }
end
end
end

def edit
respond_to do |format|
format.html { render component('users/edit').new(user: @user) }
end
Expand All @@ -47,7 +72,25 @@ def set_user
end

def user_params
params.require(:user).permit(:user_id, permitted_user_attributes)
params.require(:user).permit(
:user_id,
permitted_user_attributes,
bill_address_attributes: permitted_address_attributes,
ship_address_attributes: permitted_address_attributes
)
end

# @note This method is used to generate validation errors on the address.
# Since the update is being performed via the @user, and not directly on
# the @address, we sadly don't seem to get these errors automatically.
def set_address_from_params
if user_params.key?(:bill_address_attributes)
@address = Spree::Address.new(user_params[:bill_address_attributes])
@type = "bill"
elsif user_params.key?(:ship_address_attributes)
@address = Spree::Address.new(user_params[:ship_address_attributes])
@type = "ship"
end
end

def authorization_subject
Expand Down
6 changes: 6 additions & 0 deletions admin/config/locales/users.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ en:
title: "Users"
destroy:
success: "Users were successfully removed."
update_addresses:
bill:
success: "Billing Address has been successfully updated."
ship:
success: "Shipping Address has been successfully updated."
error: "Address could not be updated."
8 changes: 7 additions & 1 deletion admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@
end
end

admin_resources :users, only: [:index, :edit, :destroy]
admin_resources :users, only: [:index, :edit, :destroy] do
member do
get :addresses
put :update_addresses
end
end

admin_resources :promotions, only: [:index, :destroy]
admin_resources :properties, only: [:index, :destroy]
admin_resources :option_types, only: [:index, :destroy], sortable: true
Expand Down
52 changes: 52 additions & 0 deletions admin/spec/features/users_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,56 @@
expect(page).not_to have_content("[email protected]")
end
end

context "when editing a user's addresses" do
before do
create(:user_with_addresses, email: "[email protected]")
visit "/admin/users"
find_row("[email protected]").click
click_on "Addresses"
end

it "shows the address page" do
expect(page).to have_content("Users / [email protected] / Addresses")
expect(page).to have_content("Lifetime Stats")
expect(page).to have_content("Billing Address")
expect(page).to be_axe_clean
end

it "allows editing of the existing address" do
# Invalid submission
within("form.ship_address") do
fill_in "Name", with: ""
fill_in "Street Address", with: ""
click_on "Update"
end
expect(page).to have_content("can't be blank").twice

# Valid submission
within("form.bill_address") do
fill_in "Name", with: "Galadriel"
click_on "Update"
end
expect(page).to have_content("Billing Address has been successfully updated.")

# Valid submission
within("form.ship_address") do
fill_in "Name", with: "Elrond"
click_on "Update"
end
expect(page).to have_content("Shipping Address has been successfully updated.")

# Cancel submission
within("form.bill_address") do
fill_in "Name", with: "Smeagol"
click_on "Cancel"
end
expect(page).to have_content("Users / [email protected] / Addresses")
expect(page).not_to have_content("Smeagol")

# The address forms weirdly only have values rather than actual text on the page.
expect(page).to have_field("user[bill_address_attributes][name]", with: "Galadriel")
expect(page).to have_field("user[ship_address_attributes][name]", with: "Elrond")
end
end
end
Loading
Loading