Member index: filter by organizations (#17943)
* show the organizations checkboxes * apply clear filter button and indicator * filter by org in users query * add some rspec tests * add cypress specs * correct cypress spec * Fixing UsersQuery to ensure singular user by organization Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
This commit is contained in:
parent
c1a95e4595
commit
d57e4cabb7
5 changed files with 112 additions and 8 deletions
|
|
@ -35,9 +35,11 @@ module Admin
|
|||
search: params[:search],
|
||||
role: params[:role],
|
||||
roles: params[:roles],
|
||||
organizations: params[:organizations],
|
||||
).page(params[:page]).per(50)
|
||||
|
||||
@organization_limit = 3
|
||||
@organizations = Organization.order(name: :desc)
|
||||
end
|
||||
|
||||
def edit
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ module Admin
|
|||
# @param role [String, nil]
|
||||
# @param search [String, nil]
|
||||
# @param roles [Array<String>, nil]
|
||||
def self.call(relation: User.registered, role: nil, search: nil, roles: [])
|
||||
# @param organizations [Array<String>, nil]
|
||||
def self.call(relation: User.registered, role: nil, search: nil, roles: [], organizations: [])
|
||||
# We are at an interstitial moment where we are exposing both the role and roles param. We
|
||||
# need to favor one or the other.
|
||||
if role.presence
|
||||
|
|
@ -18,6 +19,10 @@ module Admin
|
|||
relation = filter_roles(relation: relation, roles: roles)
|
||||
end
|
||||
|
||||
if organizations.presence
|
||||
relation = filter_organization_memberships(relation: relation, organizations: organizations)
|
||||
end
|
||||
|
||||
relation = search_relation(relation, search) if search.presence
|
||||
relation.distinct.order(created_at: :desc)
|
||||
end
|
||||
|
|
@ -26,6 +31,11 @@ module Admin
|
|||
relation.where(QUERY_CLAUSE, search: "%#{search.strip}%")
|
||||
end
|
||||
|
||||
def self.filter_organization_memberships(relation:, organizations:)
|
||||
sub_query = OrganizationMembership.select(:user_id).where(organization_id: organizations)
|
||||
relation.where(id: sub_query)
|
||||
end
|
||||
|
||||
# Apply the "where" scope to the given relation for the given roles.
|
||||
#
|
||||
# @param relation [ActiveRecord::Relation<User>]
|
||||
|
|
|
|||
|
|
@ -50,8 +50,26 @@
|
|||
Last activity options
|
||||
</details>
|
||||
<details data-section="organizations" class="admin-details">
|
||||
<summary class="py-4 flex justify-between text-uppercase color-base-60 fs-s">Organizations<%= crayons_icon_tag("chevron-down", aria_hidden: true, class: "summary-icon") %></summary>
|
||||
Organizations options
|
||||
<summary class="py-4 flex justify-between text-uppercase color-base-60 fs-s">
|
||||
<span class="flex">
|
||||
Organizations
|
||||
<span class="js-filtered-indicator-organizations relative ml-2 <%= params[:organizations].blank? ? "hidden" : "" %>">
|
||||
<span class="block absolute c-indicator c-indicator--info"></span>
|
||||
</span>
|
||||
</span>
|
||||
<%= crayons_icon_tag("chevron-down", aria_hidden: true, class: "summary-icon") %>
|
||||
</summary>
|
||||
<fieldset class="js-organizations-fieldset pb-4 flex flex-col items-start">
|
||||
<legend class="screen-reader-only">Organizations</legend>
|
||||
<% @organizations.each_with_index do |org, index| %>
|
||||
<div class="crayons-field crayons-field--checkbox">
|
||||
<%= f.check_box :organizations, { multiple: true, class: "crayons-checkbox", id: "filter-org-#{index}", checked: params[:organizations]&.include?(org.id.to_s) }, org.id, false %>
|
||||
<%= f.label :organizations, for: "filter-org-#{index}", class: "crayons-field__label" do %><%= org.name %><% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
<button type="button" class="js-clear-filter-btn c-btn <%= params[:organizations].blank? ? "hidden" : "" %>" data-checkbox-fieldset-selector=".js-organizations-fieldset" data-filter-indicator-selector=".js-filtered-indicator-organizations" aria-label="Clear organizations filter">Clear filter</button>
|
||||
|
||||
</details>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-auto">
|
||||
|
|
|
|||
|
|
@ -29,20 +29,31 @@ describe('Filter user index', () => {
|
|||
cy.findAllByRole('button', { name: 'Filter' }).last().click();
|
||||
cy.getModal().within(() => {
|
||||
cy.findAllByText('Member roles').first().click();
|
||||
cy.findByRole('group', { name: 'Member roles' }).should('be.visible');
|
||||
cy.findByRole('group', { name: 'Member roles' })
|
||||
.as('memberRoles')
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('@memberRoles')
|
||||
.findAllByRole('checkbox')
|
||||
.should('have.length', 6);
|
||||
|
||||
cy.findAllByRole('checkbox').should('have.length', 6);
|
||||
cy.findByRole('button', { name: 'See more roles' })
|
||||
.as('seeMoreButton')
|
||||
.should('have.attr', 'aria-pressed', 'false')
|
||||
.click()
|
||||
.should('have.attr', 'aria-pressed', 'true');
|
||||
|
||||
cy.findAllByRole('checkbox').should('have.length', 16);
|
||||
cy.get('@memberRoles')
|
||||
.findAllByRole('checkbox')
|
||||
.should('have.length', 16);
|
||||
|
||||
cy.get('@seeMoreButton')
|
||||
.click()
|
||||
.should('have.attr', 'aria-pressed', 'false');
|
||||
cy.findAllByRole('checkbox').should('have.length', 6);
|
||||
|
||||
cy.get('@memberRoles')
|
||||
.findAllByRole('checkbox')
|
||||
.should('have.length', 6);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -100,4 +111,40 @@ describe('Filter user index', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Organizations', () => {
|
||||
it('filters by organizations', () => {
|
||||
cy.findAllByRole('button', { name: 'Filter' }).last().click();
|
||||
cy.getModal().within(() => {
|
||||
cy.findAllByText('Organizations').first().click();
|
||||
cy.findByRole('group', { name: 'Organizations' }).should('be.visible');
|
||||
|
||||
cy.findByRole('checkbox', { name: 'Bachmanity' }).check();
|
||||
cy.findByRole('checkbox', { name: 'Awesome Org' }).check();
|
||||
cy.findByRole('button', { name: 'Apply filters' }).click();
|
||||
});
|
||||
|
||||
cy.findAllByRole('row').should('have.length', 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multiple filters', () => {
|
||||
it('filters by multiple criteria', () => {
|
||||
cy.findAllByRole('button', { name: 'Filter' }).last().click();
|
||||
cy.getModal().within(() => {
|
||||
cy.findAllByText('Member roles').first().click();
|
||||
cy.findByRole('group', { name: 'Member roles' }).should('be.visible');
|
||||
cy.findByRole('checkbox', { name: 'Super Admin' }).check();
|
||||
|
||||
cy.findAllByText('Organizations').first().click();
|
||||
cy.findByRole('group', { name: 'Organizations' }).should('be.visible');
|
||||
|
||||
cy.findByRole('checkbox', { name: 'Bachmanity' }).check();
|
||||
cy.findByRole('checkbox', { name: 'Awesome Org' }).check();
|
||||
cy.findByRole('button', { name: 'Apply filters' }).click();
|
||||
});
|
||||
|
||||
cy.findAllByRole('row').should('have.length', 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Admin::UsersQuery, type: :query do
|
||||
subject { described_class.call(search: search, role: role, roles: roles) }
|
||||
subject { described_class.call(search: search, role: role, roles: roles, organizations: organizations) }
|
||||
|
||||
let(:role) { nil }
|
||||
let(:roles) { [] }
|
||||
let(:organizations) { [] }
|
||||
let(:search) { [] }
|
||||
|
||||
let!(:org1) { create(:organization, name: "Org1") }
|
||||
let!(:org2) { create(:organization, name: "Org2") }
|
||||
|
||||
let!(:user) { create(:user, :trusted, name: "Greg") }
|
||||
let!(:user2) { create(:user, :trusted, name: "Gregory") }
|
||||
let!(:user3) { create(:user, :tag_moderator, name: "Paul") }
|
||||
|
|
@ -64,5 +68,28 @@ RSpec.describe Admin::UsersQuery, type: :query do
|
|||
|
||||
it { is_expected.to eq([user8, user7, user6, user5, user4]) }
|
||||
end
|
||||
|
||||
context "when given organizations" do
|
||||
before do
|
||||
create(:organization_membership, user: user, organization: org1, type_of_user: "member")
|
||||
create(:organization_membership, user: user2, organization: org2, type_of_user: "member")
|
||||
end
|
||||
|
||||
let(:organizations) { [org1.id, org2.id] }
|
||||
|
||||
it { is_expected.to eq([user2, user]) }
|
||||
end
|
||||
|
||||
context "when given organizations and roles" do
|
||||
before do
|
||||
create(:organization_membership, user: user, organization: org1, type_of_user: "member")
|
||||
create(:organization_membership, user: user4, organization: org2, type_of_user: "member")
|
||||
end
|
||||
|
||||
let(:organizations) { [org1.id, org2.id] }
|
||||
let(:roles) { ["Admin"] }
|
||||
|
||||
it { is_expected.to eq([user4]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue