Skip to content

Commit 7593683

Browse files
Merge pull request #5869 from MadelineCollier/admin-user-admin-order-history
[Admin] Add new users admin order history page
2 parents 489e527 + 75aac68 commit 7593683

File tree

9 files changed

+287
-5
lines changed

9 files changed

+287
-5
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def tabs
2727
},
2828
{
2929
text: t('.order_history'),
30-
href: spree.orders_admin_user_path(@user),
30+
href: solidus_admin.orders_user_path(@user),
3131
current: false,
3232
},
3333
{

admin/app/components/solidus_admin/users/edit/component.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ def tabs
2525
},
2626
{
2727
text: t('.order_history'),
28-
href: spree.orders_admin_user_path(@user),
29-
# @todo: update this "current" logic once folded into new admin
30-
current: action_name != "edit",
28+
href: solidus_admin.orders_user_path(@user),
29+
current: action_name == "orders",
3130
},
3231
{
3332
text: t('.items'),
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<%= page do %>
2+
<%= page_header do %>
3+
<%= page_header_back(solidus_admin.users_path) %>
4+
<%= page_header_title(t(".title", email: @user.email)) %>
5+
6+
<%= page_header_actions do %>
7+
<%= render component("ui/button").new(tag: :a, text: t(".create_order_for_user"), href: spree.new_admin_order_path(user_id: @user.id)) %>
8+
<% end %>
9+
<% end %>
10+
11+
<%= page_header do %>
12+
<% tabs.each do |tab| %>
13+
<%= render(component("ui/button").new(tag: :a, scheme: :ghost, text: tab[:text], 'aria-current': tab[:current], href: tab[:href])) %>
14+
<% end %>
15+
<% end %>
16+
17+
<%= page_with_sidebar do %>
18+
<%= page_with_sidebar_main do %>
19+
<%= render component('ui/panel').new(title: t(".order_history")) do %>
20+
<% if @orders.present? %>
21+
<%= render component('ui/table').new(
22+
id: stimulus_id,
23+
data: {
24+
class: model_class,
25+
rows: rows,
26+
fade: -> { row_fade(_1) },
27+
columns: columns,
28+
url: -> { row_url(_1) },
29+
},
30+
)%>
31+
<% else %>
32+
<%= t(".no_orders_found") %>
33+
<%= render component("ui/button").new(tag: :a, text: t(".create_one"), href: spree.new_admin_order_path(user_id: @user.id)) %>
34+
<% end %>
35+
<% end %>
36+
<% end %>
37+
38+
<%= page_with_sidebar_aside do %>
39+
<%= render component("ui/panel").new(title: t("spree.lifetime_stats")) do %>
40+
<%= render component("ui/details_list").new(
41+
items: [
42+
{ label: t("spree.total_sales"), value: @user.display_lifetime_value.to_html },
43+
{ label: t("spree.order_count"), value: @user.order_count.to_i },
44+
{ label: t("spree.average_order_value"), value: @user.display_average_order_value.to_html },
45+
{ label: t("spree.member_since"), value: @user.created_at.to_date },
46+
{ label: t(".last_active"), value: last_login(@user) },
47+
]
48+
) %>
49+
<% end %>
50+
<% end %>
51+
<% end %>
52+
<% end %>
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# frozen_string_literal: true
2+
3+
class SolidusAdmin::Users::Orders::Component < SolidusAdmin::BaseComponent
4+
include SolidusAdmin::Layout::PageHelpers
5+
6+
def initialize(user:, orders:)
7+
@user = user
8+
@orders = orders
9+
end
10+
11+
def form_id
12+
@form_id ||= "#{stimulus_id}--form-#{@user.id}"
13+
end
14+
15+
def tabs
16+
[
17+
{
18+
text: t('.account'),
19+
href: solidus_admin.user_path(@user),
20+
current: false,
21+
},
22+
{
23+
text: t('.addresses'),
24+
href: solidus_admin.addresses_user_path(@user),
25+
current: false,
26+
},
27+
{
28+
text: t('.order_history'),
29+
href: solidus_admin.orders_user_path(@user),
30+
current: true,
31+
},
32+
{
33+
text: t('.items'),
34+
href: spree.items_admin_user_path(@user),
35+
current: false,
36+
},
37+
{
38+
text: t('.store_credit'),
39+
href: spree.admin_user_store_credits_path(@user),
40+
current: false,
41+
},
42+
]
43+
end
44+
45+
def last_login(user)
46+
return t('.last_login.never') if user.try(:last_sign_in_at).blank?
47+
48+
t(
49+
'.last_login.login_time_ago',
50+
# @note The second `.try` is only here for the specs to work.
51+
last_login_time: time_ago_in_words(user.try(:last_sign_in_at))
52+
).capitalize
53+
end
54+
55+
def model_class
56+
Spree::Order
57+
end
58+
59+
def row_url(order)
60+
spree.edit_admin_order_path(order)
61+
end
62+
63+
def rows
64+
@orders
65+
end
66+
67+
def row_fade(_order)
68+
false
69+
end
70+
71+
def columns
72+
[
73+
number_column,
74+
state_column,
75+
date_column,
76+
payment_column,
77+
shipment_column,
78+
total_column,
79+
]
80+
end
81+
82+
def number_column
83+
{
84+
header: :order,
85+
data: ->(order) do
86+
if !row_fade(order)
87+
content_tag :div, order.number, class: 'font-semibold'
88+
else
89+
content_tag :div, order.number
90+
end
91+
end
92+
}
93+
end
94+
95+
def state_column
96+
{
97+
header: :state,
98+
data: ->(order) do
99+
color = {
100+
'complete' => :green,
101+
'returned' => :red,
102+
'canceled' => :blue,
103+
'cart' => :graphite_light,
104+
}[order.state] || :yellow
105+
component('ui/badge').new(name: order.state.humanize, color: color)
106+
end
107+
}
108+
end
109+
110+
def date_column
111+
{
112+
header: :date,
113+
data: ->(order) do
114+
content_tag :div, l(order.created_at, format: :short)
115+
end
116+
}
117+
end
118+
119+
def total_column
120+
{
121+
header: :total,
122+
data: ->(order) do
123+
content_tag :div, number_to_currency(order.total)
124+
end
125+
}
126+
end
127+
128+
def payment_column
129+
{
130+
header: :payment,
131+
data: ->(order) do
132+
component('ui/badge').new(name: order.payment_state.humanize, color: order.paid? ? :green : :yellow) if order.payment_state?
133+
end
134+
}
135+
end
136+
137+
def shipment_column
138+
{
139+
header: :shipment,
140+
data: ->(order) do
141+
component('ui/badge').new(name: order.shipment_state.humanize, color: order.shipped? ? :green : :yellow) if order.shipment_state?
142+
end
143+
}
144+
end
145+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
en:
2+
title: "Users / %{email} / Order History"
3+
account: Account
4+
addresses: Addresses
5+
order_history: Order History
6+
items: Items
7+
store_credit: Store Credit
8+
last_active: Last Active
9+
last_login:
10+
login_time_ago: "%{last_login_time} ago"
11+
never: Never
12+
invitation_sent: Invitation sent
13+
create_order_for_user: Create order for this user
14+
no_orders_found: No Orders found.
15+
create_one: Create One
16+
back: Back

admin/app/controllers/solidus_admin/users_controller.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class UsersController < SolidusAdmin::BaseController
55
include SolidusAdmin::ControllerHelpers::Search
66
include Spree::Core::ControllerHelpers::StrongParameters
77

8-
before_action :set_user, only: [:edit, :addresses, :update_addresses]
8+
before_action :set_user, only: [:edit, :addresses, :update_addresses, :orders]
99

1010
search_scope(:all, default: true)
1111
search_scope(:customers) { _1.left_outer_joins(:role_users).where(role_users: { id: nil }) }
@@ -50,6 +50,14 @@ def update_addresses
5050
end
5151
end
5252

53+
def orders
54+
set_orders
55+
56+
respond_to do |format|
57+
format.html { render component('users/orders').new(user: @user, orders: @orders) }
58+
end
59+
end
60+
5361
def edit
5462
respond_to do |format|
5563
format.html { render component('users/edit').new(user: @user) }
@@ -93,6 +101,12 @@ def set_address_from_params
93101
end
94102
end
95103

104+
def set_orders
105+
params[:q] ||= {}
106+
@search = Spree::Order.reverse_chronological.ransack(params[:q].merge(user_id_eq: @user.id))
107+
@orders = @search.result.page(params[:page]).per(Spree::Config[:admin_products_per_page])
108+
end
109+
96110
def authorization_subject
97111
Spree.user_class
98112
end

admin/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
member do
5050
get :addresses
5151
put :update_addresses
52+
get :orders
5253
end
5354
end
5455

admin/spec/features/users_spec.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,49 @@
161161
expect(page).to have_field("user[ship_address_attributes][name]", with: "Elrond")
162162
end
163163
end
164+
165+
context "when viewing a user's order history" do
166+
context "when a user has no orders" do
167+
before do
168+
create(:user, email: "[email protected]")
169+
visit "/admin/users"
170+
find_row("[email protected]").click
171+
click_on "Order History"
172+
end
173+
174+
it "shows the order history page" do
175+
expect(page).to have_content("Users / [email protected] / Order History")
176+
expect(page).to have_content("Lifetime Stats")
177+
expect(page).to have_content("Order History")
178+
expect(page).to be_axe_clean
179+
end
180+
181+
it "shows the appropriate content" do
182+
expect(page).to have_content("No Orders found.")
183+
end
184+
end
185+
186+
context "when a user has ordered before" do
187+
before do
188+
create(:user, :with_orders, email: "[email protected]")
189+
visit "/admin/users"
190+
find_row("[email protected]").click
191+
click_on "Order History"
192+
end
193+
194+
it "shows the order history page" do
195+
expect(page).to have_content("Users / [email protected] / Order History")
196+
expect(page).to have_content("Lifetime Stats")
197+
expect(page).to have_content("Order History")
198+
expect(page).to be_axe_clean
199+
end
200+
201+
it "shows the order history" do
202+
expect(page).to have_content(/R\d+/) # Matches on any order number.
203+
expect(page).to have_content("Shipment")
204+
expect(page).to have_content("Payment")
205+
expect(page).not_to have_content("No Orders found.")
206+
end
207+
end
208+
end
164209
end

admin/spec/requests/solidus_admin/users_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@
5959
end
6060
end
6161

62+
describe "GET /orders" do
63+
let!(:order) { create(:order, user: user) }
64+
65+
it "renders the orders template and displays the user's orders" do
66+
get solidus_admin.orders_user_path(user)
67+
expect(response).to have_http_status(:ok)
68+
expect(response.body).to include(order.number)
69+
end
70+
end
71+
6272
describe "DELETE /destroy" do
6373
it "deletes the user and redirects to the index page with a 303 See Other status" do
6474
# Ensure the user exists prior to deletion

0 commit comments

Comments
 (0)