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] New admin user edit page #5856

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,49 @@
<%= render component('ui/panel').new(title: t('.api_access')) do %>
<section>
<% if @user.spree_api_key.present? %>
<div id="current-api-key">
<h2 class="py-1.5 font-semibold"><%= t('.key') %></h2>
<% if @user == helpers.current_solidus_admin_user %>
<%= @user.spree_api_key %>
<% else %>
<i>(<%= t('spree.hidden') %>)</i>
<% end %>
</div>

<div class="py-1.5 text-center">
<%= form_with url: spree.admin_user_api_key_path(@user), method: :delete, local: true, html: { class: 'clear_api_key inline-flex' } do %>
<%= render component("ui/button").new(
text: t('.clear_key'),
scheme: :secondary,
type: :submit,
"data-action": "click->#{stimulus_id}#confirm",
"data-#{stimulus_id}-message-param": t(".confirm_clear_key"),
) %>
<% end %>

<%= form_with url: spree.admin_user_api_key_path(@user), method: :post, local: true, html: { class: 'regen_api_key inline-flex' } do %>
<%= render component("ui/button").new(
text: t('.regenerate_key'),
scheme: :secondary,
type: :submit,
"data-action": "click->#{stimulus_id}#confirm",
"data-#{stimulus_id}-message-param": t(".confirm_regenerate_key"),
) %>
<% end %>
</div>

<% else %>
<div class="no-objects-found"><%= t('.no_key') %></div>
<div class="filter-actions actions">
<div class="py-1.5 text-center">
<%= form_with url: spree.admin_user_api_key_path(@user), method: :post, local: true, html: { class: 'generate_api_key inline-flex' } do %>
<%= render component("ui/button").new(
text: t('.generate_key'),
type: :submit,
) %>
<% end %>
</div>
</div>
<% end %>
</section>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
confirm(event) {
if (!confirm(event.params.message)) {
event.preventDefault()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class SolidusAdmin::Users::Edit::ApiAccess::Component < SolidusAdmin::BaseComponent
def initialize(user:)
@user = user
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
en:
api_access: API Access
no_key: No key
key: "Key"
generate_key: Generate API key
clear_key: Clear key
regenerate_key: Regenerate key
hidden: Hidden
confirm_clear_key: Are you sure you want to clear this user's API key? It will invalidate the existing key.
confirm_regenerate_key: Are you sure you want to regenerate this user's API key? It will invalidate the existing key.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<%= page do %>
<%= page_header do %>
<%= page_header_back(solidus_admin.users_path) %>
<%= page_header_title(t(".title", email: @user.email)) %>

<% # @todo: I am not sure how we want to handle Cancan stuff in the new admin. %>
<% # if can?(:admin, Spree::Order) && can?(:create, Spree::Order) %>
<%= 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 %>
<% 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: Spree.user_class.model_name.human) do %>
<%= form_for @user, url: solidus_admin.user_path(@user), html: { id: form_id, autocomplete: "off" } do |f| %>
<div class="py-1.5">
<%= render component("ui/forms/field").text_field(f, :email) %>
</div>
<div class="py-1.5">
<%= render component("ui/forms/field").text_field(f, :password) %>
</div>
<div class="py-1.5">
<%= render component("ui/forms/field").text_field(f, :password_confirmation) %>
</div>
<div class="py-1.5">
<%= render component("ui/checkbox_row").new(options: role_options, row_title: "Roles", form: f, method: "spree_role_ids", layout: :subsection) %>
</div>
<div class="py-1.5 text-center">
<%= render component("ui/button").new(tag: :button, text: t(".update"), form: form_id) %>
<%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.user_path(@user), scheme: :secondary) %>
</div>
<% end %>
<% end %>

<%= render component("users/edit/api_access").new(user: @user) %>

<% 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 %>
63 changes: 63 additions & 0 deletions admin/app/components/solidus_admin/users/edit/component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

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

def initialize(user:)
@user = user
end

def form_id
@form_id ||= "#{stimulus_id}--form-#{@user.id}"
end

def tabs
[
{
text: t('.account'),
href: solidus_admin.users_path,
current: action_name == "show",
},
{
text: t('.addresses'),
href: spree.addresses_admin_user_path(@user),
# @todo: update this "current" logic once folded into new admin
current: action_name != "show",
},
{
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",
},
{
text: t('.items'),
href: spree.items_admin_user_path(@user),
# @todo: update this "current" logic once folded into new admin
current: action_name != "show",
},
{
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",
},
]
end

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

t(

Check warning on line 51 in admin/app/components/solidus_admin/users/edit/component.rb

View check run for this annotation

Codecov / codecov/patch

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

Added line #L51 was not covered by tests
'.last_login.login_time_ago',
# @note The second `.try` is only here for the specs to work.
tvdeyen marked this conversation as resolved.
Show resolved Hide resolved
last_login_time: time_ago_in_words(user.try(:last_sign_in_at))
).capitalize
end

def role_options
Spree::Role.all.map do |role|
{ label: role.name, id: role.id }
end
end
end
16 changes: 16 additions & 0 deletions admin/app/components/solidus_admin/users/edit/component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
en:
title: "Users / %{email}"
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
5 changes: 3 additions & 2 deletions admin/app/components/solidus_admin/users/index/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def search_url
end

def row_url(user)
spree.admin_user_path(user)
solidus_admin.edit_user_path(user)
end

def page_actions
Expand Down Expand Up @@ -104,7 +104,8 @@ def last_login(user)

t(
'.last_login.login_time_ago',
# @note The second `.try` is only here for the specs to work.
# @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
Expand Down
12 changes: 12 additions & 0 deletions admin/app/controllers/solidus_admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def index
end
end

def edit
set_user

respond_to do |format|
format.html { render component('users/edit').new(user: @user) }
end
end

def destroy
@users = Spree.user_class.where(id: params[:id])

Expand All @@ -34,6 +42,10 @@ def destroy

private

def set_user
@user = Spree.user_class.find(params[:id])
end

def user_params
params.require(:user).permit(:user_id, permitted_user_attributes)
end
Expand Down
2 changes: 1 addition & 1 deletion admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
end
end

admin_resources :users, only: [:index, :destroy]
admin_resources :users, only: [:index, :edit, :destroy]
admin_resources :promotions, only: [:index, :destroy]
admin_resources :properties, only: [:index, :destroy]
admin_resources :option_types, only: [:index, :destroy], sortable: true
Expand Down
59 changes: 58 additions & 1 deletion admin/spec/features/users_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
require "spec_helper"

describe "Users", :js, type: :feature do
before { sign_in create(:admin_user, email: "[email protected]") }
let(:admin) { create(:admin_user, email: "[email protected]") }

before do
sign_in admin
end

it "lists users and allows deleting them" do
create(:user, email: "[email protected]")
Expand Down Expand Up @@ -52,4 +56,57 @@
expect(page).not_to have_content("Never")
end
end

context "when editing an existing user" do
before do
# This is needed for the actions which are still powered by the backend
# and not the new admin. (#update, etc.)
stub_authorization!(admin)

create(:user, email: "[email protected]")
visit "/admin/users"
find_row("[email protected]").click
end

it "shows the edit page" do
expect(page).to have_content("Users / [email protected]")
expect(page).to have_content("Lifetime Stats")
expect(page).to have_content("Roles")
expect(find("label", text: /admin/i).find("input[type=checkbox]").checked?).to eq(false)
end

it "allows editing of the existing user" do
# API key interactions
expect(page).to have_content("No key")
click_on "Generate API key"
expect(page).to have_content("Key generated")
expect(page).to have_content("(hidden)")

click_on "Regenerate key"
expect(page).to have_content("Key generated")
expect(page).to have_content("(hidden)")

click_on "Clear key"
expect(page).to have_content("Key cleared")
expect(page).to have_content("No key")

# Update user
within("form.edit_user") do
fill_in "Email", with: "[email protected]"
find("label", text: /admin/i).find("input[type=checkbox]").check
click_on "Update"
end

expect(page).to have_content("Users / [email protected]")
expect(find("label", text: /admin/i).find("input[type=checkbox]").checked?).to eq(true)

# Cancel out of editing
within("form.edit_user") do
fill_in "Email", with: "[email protected]"
click_on "Cancel"
end

expect(page).not_to have_content("[email protected]")
end
end
end
Loading