Skip to content

Commit

Permalink
Merge pull request solidusio#5846 from MadelineCollier/admin-role-cre…
Browse files Browse the repository at this point in the history
…ation-with-permissions-round-2

[Admin] Allow assignment of permission sets when creating/editing admin roles
  • Loading branch information
MadelineCollier authored Sep 9, 2024
2 parents d81043a + 3239325 commit 3151b14
Show file tree
Hide file tree
Showing 56 changed files with 927 additions and 6 deletions.
16 changes: 16 additions & 0 deletions admin/app/components/solidus_admin/roles/edit/component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<%= render component("ui/forms/field").text_field(f, :description) %>
</div>

<% if permission_set_options.present? %>
<h3 class="text-l py-4 font-semibold text-gray-900">
<%= t(".choose_permissions") %>
</h3>
<% end %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:order], row_title: t('.orders'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:product], row_title: t('.products'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:stock], row_title: t('.stock'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:user], row_title: t('.customers'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:restricted_stock], row_title: t('.restricted_stock'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:configuration], row_title: t('.settings'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:other], row_title: t('.other'), form: f, method: :permission_set_ids, layout: :subsection) %>
<% modal.with_actions do %>
<form method="dialog">
<%= render component("ui/button").new(scheme: :secondary, text: t('.cancel')) %>
Expand All @@ -14,4 +29,5 @@
<% end %>
<% end %>
<% end %>
<%= render component("roles/index").new(page: @page) %>
8 changes: 8 additions & 0 deletions admin/app/components/solidus_admin/roles/edit/component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

class SolidusAdmin::Roles::Edit::Component < SolidusAdmin::BaseComponent
include SolidusAdmin::PermissionSetsHelper

def initialize(page:, role:)
@page = page
@role = role
Expand All @@ -9,4 +11,10 @@ def initialize(page:, role:)
def form_id
dom_id(@role, "#{stimulus_id}_edit_role_form")
end

private

def permission_set_options
@permission_set_options ||= organize_permissions(permission_sets: Spree::PermissionSet.all, view_label: t(".view"), edit_label: t(".edit"))
end
end
13 changes: 13 additions & 0 deletions admin/app/components/solidus_admin/roles/edit/component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,16 @@ en:
title: "Edit Role"
cancel: "Cancel"
submit: "Update Role"
edit: "Edit"
view: "View"
name_placeholder: "Enter a clear role name"
description_placeholder: "Enter a brief description"
choose_permissions: "Choose permissions"
orders: "Orders"
products: "Products"
stock: "Stock"
restricted_stock: "Restricted Stock"
customers: "Customers"
promotions: "Promotions"
settings: "Settings"
other: "Other permissions"
19 changes: 17 additions & 2 deletions admin/app/components/solidus_admin/roles/new/component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,24 @@
<%= render component("ui/modal").new(title: t(".title")) do |modal| %>
<%= form_for @role, url: solidus_admin.roles_path, html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<%= render component("ui/forms/field").text_field(f, :description) %>
<%= render component("ui/forms/field").text_field(f, :name, class: "required", placeholder: t(".name_placeholder")) %>
<%= render component("ui/forms/field").text_field(f, :description, placeholder: t(".description_placeholder")) %>
</div>

<% if permission_set_options.present? %>
<h3 class="text-l py-4 font-semibold text-gray-900">
<%= t(".choose_permissions") %>
</h3>
<% end %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:order], row_title: t('.orders'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:product], row_title: t('.products'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:stock], row_title: t('.stock'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:user], row_title: t('.customers'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:restricted_stock], row_title: t('.restricted_stock'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:configuration], row_title: t('.settings'), form: f, method: :permission_set_ids) %>
<%= render component("ui/checkbox_row").new(options: permission_set_options[:other], row_title: t('.other'), form: f, method: :permission_set_ids, layout: :subsection) %>
<% modal.with_actions do %>
<form method="dialog">
<%= render component("ui/button").new(scheme: :secondary, text: t('.cancel')) %>
Expand Down
8 changes: 8 additions & 0 deletions admin/app/components/solidus_admin/roles/new/component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

class SolidusAdmin::Roles::New::Component < SolidusAdmin::BaseComponent
include SolidusAdmin::PermissionSetsHelper

def initialize(page:, role:)
@page = page
@role = role
Expand All @@ -9,4 +11,10 @@ def initialize(page:, role:)
def form_id
dom_id(@role, "#{stimulus_id}_new_role_form")
end

private

def permission_set_options
@permission_set_options ||= organize_permissions(permission_sets: Spree::PermissionSet.all, view_label: t(".view"), edit_label: t(".edit"))
end
end
13 changes: 13 additions & 0 deletions admin/app/components/solidus_admin/roles/new/component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,16 @@ en:
title: "New Role"
cancel: "Cancel"
submit: "Add Role"
edit: "Edit"
view: "View"
name_placeholder: "Enter a clear role name"
description_placeholder: "Enter a brief description"
choose_permissions: "Choose permissions"
orders: "Orders"
products: "Products"
stock: "Stock"
restricted_stock: "Restricted Stock"
customers: "Customers"
promotions: "Promotions"
settings: "Settings"
other: "Other permissions"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<% if @layout == :default && @options.present? %>
<span class="p-2 flex hover:bg-gray-50 rounded-md">
<span class="w-1/2"><%= @row_title %></span>
<span class="w-1/2 text-right">
<% @options.each do |option| %>
<label class="px-2 cursor-pointer">
<%= @form.check_box(@method.to_s, { checked: @form.object.try(@method.to_sym)&.include?(option[:id]), multiple: true }, option[:id], nil)%>
<span class="capitalize"><%= option[:label] %></span>
</label>
<% end %>
</span>
</span>
<% end %>
<% if @layout == :subsection && @options.present? %>
<div class="hover:bg-gray-50 rounded-md">
<div class="font-semibold px-2 py-2 mt-2 mb-1"><%= @row_title %></div>
<div class="px-4">
<% @options.each do |option| %>
<div class="py-1">
<label class="cursor-pointer">
<%= @form.check_box(@method.to_s, { checked: @form.object.try(@method.to_sym)&.include?(option[:id]), multiple: true }, option[:id], nil)%>
<span class="capitalize"><%= option[:label] %></span>
</label>
</div>
<% end %>
</div>
</div>
<% end %>
11 changes: 11 additions & 0 deletions admin/app/components/solidus_admin/ui/checkbox_row/component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class SolidusAdmin::UI::CheckboxRow::Component < SolidusAdmin::BaseComponent
def initialize(options:, row_title:, form:, method:, layout: :default)
@options = options
@row_title = row_title
@form = form
@method = method
@layout = layout
end
end
2 changes: 1 addition & 1 deletion admin/app/controllers/solidus_admin/roles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def set_index_page
end

def role_params
params.require(:role).permit(:role_id, :name, :description)
params.require(:role).permit(:role_id, :name, :description, permission_set_ids: [])
end
end
end
32 changes: 32 additions & 0 deletions admin/app/helpers/solidus_admin/permission_sets_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module SolidusAdmin
module PermissionSetsHelper
# @param permission_sets [Array<Spree::PermissionSet>] an array of
# PermissionSet objects to be organized into categories based on their
# names.
# @param view_label [String] A string of your choice associated with "View"
# or "Display" level permissions. Used when rendering the checkbox.
# @param edit_label [String] A string of your choice associated with "Edit"
# or "Management" level permissions. Used when rendering the checkbox.
def organize_permissions(permission_sets:, view_label:, edit_label:)
return {} if permission_sets.blank?

permission_sets.each_with_object({}) do |permission, grouped_permissions|
group_key = permission.category.to_sym

case permission.privilege
when "display"
grouped_permissions[group_key] ||= []
grouped_permissions[group_key] << { label: view_label, id: permission.id }
when "management"
grouped_permissions[group_key] ||= []
grouped_permissions[group_key] << { label: edit_label, id: permission.id }
else
grouped_permissions[:other] ||= []
grouped_permissions[:other] << { label: permission.name, id: permission.id }
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

# @component "ui/checkbox_row"
class SolidusAdmin::UI::CheckboxRow::ComponentPreview < ViewComponent::Preview
include SolidusAdmin::Preview

def overview
render_with_template
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
Default Layout
</h6>

<%= render current_component.new(options: [{ label: "true", id: 1 }, { label: "false", id: 0 }], row_title: "I have flossed recently", form: ActionView::Helpers::FormBuilder.new(:object, Object.new, self, {}), method: "attribute", layout: :default) %>
</div>

<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
Subsection Layout
</h6>

<%= render current_component.new(options: [{ label: "Pizza", id: 1 }, { label: "Pasta", id: 0 }, { label: "Soup", id: 3 }], row_title: "Favourite foods", form: ActionView::Helpers::FormBuilder.new(:object, Object.new, self, {}), method: "attribute", layout: :subsection) %>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe SolidusAdmin::UI::CheckboxRow::Component, type: :component do
it "renders the overview preview" do
render_preview(:overview)
end
end
49 changes: 47 additions & 2 deletions admin/spec/features/roles_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@
require 'spec_helper'

describe "Roles", :js, type: :feature do
before { sign_in create(:admin_user, email: '[email protected]') }
before do
sign_in create(:admin_user, email: '[email protected]')
end

let!(:settings_edit_permission) {
Spree::PermissionSet.find_or_create_by!(
name: "ConfigurationManagement",
set: "Spree::PermissionSets::ConfigurationManagement",
privilege: "management",
category: "configuration"
)
}
let!(:settings_view_permission) {
Spree::PermissionSet.find_or_create_by!(
name: "ConfigurationDisplay",
set: "Spree::PermissionSets::ConfigurationDisplay",
privilege: "display",
category: "configuration"
)
}

it "lists roles and allows deleting them" do
create(:role, name: "Customer Role" )
Expand Down Expand Up @@ -51,10 +70,20 @@
fill_in "Name", with: "Purchaser"
fill_in "Description", with: "A person who buys stuff"

within("form.new_role") do
expect(page).to have_content("Choose permissions")
expect(page).to have_content("Settings")
expect(page).to have_content("Edit")
expect(page).to have_content("View")
find('label', text: 'View').find('input[type=checkbox]').click
end

click_on "Add Role"

expect(page).to have_content("Role was successfully created.")
expect(Spree::Role.find_by(name: "Purchaser")).to be_present
expect(Spree::Role.find_by(name: "Purchaser").permission_set_ids)
.to contain_exactly(settings_view_permission.id)
expect(page.current_url).to include(query)
end
end
Expand Down Expand Up @@ -89,11 +118,13 @@
let(:query) { "?page=1&q%5Bname_cont%5D=er" }

before do
Spree::Role.create(name: "Reviewer")
Spree::Role.create(name: "Reviewer", permission_sets: [settings_edit_permission])
visit "/admin/roles#{query}"
find_row("Reviewer").click
expect(page).to have_content("Edit Role")
expect(page).to be_axe_clean
expect(Spree::Role.find_by(name: "Reviewer").permission_set_ids)
.to contain_exactly(settings_edit_permission.id)
end

it "opens a modal" do
Expand All @@ -107,12 +138,26 @@
fill_in "Name", with: "Publisher"
fill_in "Description", with: "A person who publishes stuff"

within("form.edit_role") do
expect(page).to have_content("Choose permissions")
expect(page).to have_content("Settings")
expect(page).to have_content("Edit")
expect(page).to have_content("View")
expect(find('label', text: 'Edit').find('input[type=checkbox]').checked?).to eq(true)
find('label', text: 'Edit').find('input[type=checkbox]').uncheck
find('label', text: 'View').find('input[type=checkbox]').check
end

click_on "Update Role"
expect(page).to have_content("Role was successfully updated.")
expect(page).to have_content("Publisher")
expect(page).to have_content("A person who publishes stuff")
expect(page).not_to have_content("Reviewer")
expect(Spree::Role.find_by(name: "Publisher")).to be_present
expect(Spree::Role.find_by(name: "Publisher").permission_set_ids)
.to contain_exactly(
settings_view_permission.id,
)
expect(page.current_url).to include(query)
end
end
Expand Down
Loading

0 comments on commit 3151b14

Please sign in to comment.