diff --git a/app/assets/stylesheets/shared/content_pane.scss b/app/assets/stylesheets/shared/content_pane.scss
index 912685141..7ce218414 100644
--- a/app/assets/stylesheets/shared/content_pane.scss
+++ b/app/assets/stylesheets/shared/content_pane.scss
@@ -21,6 +21,10 @@
&.user-groups-table-container {
height: calc(100vh - var(--content-header-size) - var(--navbar-height) - 72px);
}
+
+ &.user-group-table-container {
+ height: calc(100vh - var(--content-header-size) - var(--navbar-height) - 104px);
+ }
}
.content-header {
diff --git a/app/controllers/users/settings/user_group_memberships_controller.rb b/app/controllers/users/settings/user_group_memberships_controller.rb
index c05070bbf..c36021087 100644
--- a/app/controllers/users/settings/user_group_memberships_controller.rb
+++ b/app/controllers/users/settings/user_group_memberships_controller.rb
@@ -3,11 +3,25 @@
module Users
module Settings
class UserGroupMembershipsController < ApplicationController
- before_action :load_team, except: :index
+ before_action :load_team
before_action :load_user_group
before_action :check_manage_permissions, except: %i(index show)
- def index; end
+ def index
+ memberships = Lists::UserGroupMembershipsService.new(@user_group.user_group_memberships, params).call
+ render json: memberships, each_serializer: Lists::UserGroupMembershipSerializer, user: current_user, meta: pagination_dict(memberships)
+ end
+
+ def actions_toolbar
+ render json: {
+ actions:
+ Toolbars::UserGroupMembershipsService.new(
+ current_user,
+ @user_group,
+ user_group_membership_ids: JSON.parse(params[:items]).pluck('id')
+ ).actions
+ }
+ end
def show; end
@@ -27,7 +41,15 @@ module Users
end
end
- def destroy; end
+ def destroy_multiple
+ members = @user_group.user_group_memberships.where(id: params[:membership_ids])
+
+ if members.destroy_all
+ render json: { message: :success }, status: :ok
+ else
+ head :unprocessable_entity
+ end
+ end
private
diff --git a/app/controllers/users/settings/user_groups_controller.rb b/app/controllers/users/settings/user_groups_controller.rb
index 85f271749..83731ceed 100644
--- a/app/controllers/users/settings/user_groups_controller.rb
+++ b/app/controllers/users/settings/user_groups_controller.rb
@@ -39,7 +39,9 @@ module Users
end
end
- def show; end
+ def show
+ @active_tab = :user_groups
+ end
def create
@user_group = @team.user_groups.new
diff --git a/app/javascript/packs/vue/user_groups_show.js b/app/javascript/packs/vue/user_groups_show.js
new file mode 100644
index 000000000..f4929ca5c
--- /dev/null
+++ b/app/javascript/packs/vue/user_groups_show.js
@@ -0,0 +1,10 @@
+import { createApp } from 'vue/dist/vue.esm-bundler.js';
+import PerfectScrollbar from 'vue3-perfect-scrollbar';
+import UserGroupShow from '../../vue/user_groups/show.vue';
+import { mountWithTurbolinks } from './helpers/turbolinks.js';
+
+const app = createApp();
+app.component('UserGroupShow', UserGroupShow);
+app.config.globalProperties.i18n = window.I18n;
+app.use(PerfectScrollbar);
+mountWithTurbolinks(app, '#userGroupShow');
diff --git a/app/javascript/vue/user_groups/modal/add_member.vue b/app/javascript/vue/user_groups/modal/add_member.vue
new file mode 100644
index 000000000..2b70dc230
--- /dev/null
+++ b/app/javascript/vue/user_groups/modal/add_member.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
diff --git a/app/javascript/vue/user_groups/modal/create_group.vue b/app/javascript/vue/user_groups/modal/create_group.vue
index a8c3f548d..0b9aaa0e8 100644
--- a/app/javascript/vue/user_groups/modal/create_group.vue
+++ b/app/javascript/vue/user_groups/modal/create_group.vue
@@ -78,25 +78,11 @@ export default {
computed: {
validName() {
return this.name.length >= GLOBAL_CONSTANTS.NAME_MIN_LENGTH;
- },
- modalHeader() {
- if (this.createUrl) {
- return this.i18n.t('projects.index.modal_new_project.modal_title');
- }
-
- return this.i18n.t('projects.index.modal_edit_project.modal_title', { project: this.project?.name });
- },
- submitButtonLabel() {
- if (this.createUrl) {
- return this.i18n.t('projects.index.modal_new_project.create');
- }
-
- return this.i18n.t('projects.index.modal_edit_project.submit');
}
},
data() {
return {
- name: this.project?.name || '',
+ name: '',
users: [],
submitting: false
};
diff --git a/app/javascript/vue/user_groups/show.vue b/app/javascript/vue/user_groups/show.vue
new file mode 100644
index 000000000..ffb7c5fb8
--- /dev/null
+++ b/app/javascript/vue/user_groups/show.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/models/user_group_membership.rb b/app/models/user_group_membership.rb
index 293db6444..fb0629b27 100644
--- a/app/models/user_group_membership.rb
+++ b/app/models/user_group_membership.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class UserGroupMembership < ApplicationRecord
+ include SearchableModel
+
belongs_to :user_group
belongs_to :user
belongs_to :created_by, class_name: 'User'
diff --git a/app/serializers/lists/user_group_membership_serializer.rb b/app/serializers/lists/user_group_membership_serializer.rb
new file mode 100644
index 000000000..1726959e5
--- /dev/null
+++ b/app/serializers/lists/user_group_membership_serializer.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Lists
+ class UserGroupMembershipSerializer < ActiveModel::Serializer
+ include Rails.application.routes.url_helpers
+ include Canaid::Helpers::PermissionsHelper
+
+ attributes :id, :created_at, :name, :email
+
+ def name
+ object.user.full_name
+ end
+
+ def email
+ object.user.email
+ end
+
+ def created_at
+ I18n.l(object.created_at, format: :full_date)
+ end
+ end
+end
diff --git a/app/services/lists/user_group_memberships_service.rb b/app/services/lists/user_group_memberships_service.rb
new file mode 100644
index 000000000..49dba47c8
--- /dev/null
+++ b/app/services/lists/user_group_memberships_service.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Lists
+ class UserGroupMembershipsService < BaseService
+ private
+
+ def fetch_records
+ @records = @raw_data.joins(:user).includes(:user)
+ end
+
+ def filter_records
+ return unless @params[:search]
+
+ @records = @records.where_attributes_like(
+ ['users.full_name', 'users.email'],
+ @params[:search]
+ )
+ end
+
+ def sortable_columns
+ @sortable_columns ||= {
+ name: 'users.full_name',
+ email: 'users.email',
+ created_at: 'user_group_memberships.created_at'
+ }
+ end
+ end
+end
diff --git a/app/services/toolbars/user_group_memberships_service.rb b/app/services/toolbars/user_group_memberships_service.rb
new file mode 100644
index 000000000..191c2ad61
--- /dev/null
+++ b/app/services/toolbars/user_group_memberships_service.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Toolbars
+ class UserGroupMembershipsService
+ attr_reader :current_user
+
+ include Canaid::Helpers::PermissionsHelper
+ include Rails.application.routes.url_helpers
+
+ def initialize(current_user, user_group, user_group_membership_ids: [])
+ @current_user = current_user
+ @user_group = user_group
+ @team = user_group.team
+ @memberships = @user_group.user_group_memberships.where(id: user_group_membership_ids)
+
+ @single = @memberships.length == 1
+ end
+
+ def actions
+ return [] if @memberships.none?
+
+ [
+ delete_action
+ ].compact
+ end
+
+ private
+
+ def delete_action
+ return unless can_manage_team?(@team)
+
+ {
+ name: 'delete',
+ label: 'Remove',
+ icon: 'sn-icon sn-icon-delete',
+ path: destroy_multiple_users_settings_team_user_group_user_group_memberships_path(@team, @user_group, membership_ids: @memberships.pluck(:id)),
+ type: :emit
+ }
+ end
+ end
+end
diff --git a/app/views/users/settings/user_groups/show.html.erb b/app/views/users/settings/user_groups/show.html.erb
new file mode 100644
index 000000000..ed7ce355b
--- /dev/null
+++ b/app/views/users/settings/user_groups/show.html.erb
@@ -0,0 +1,28 @@
+<% provide(:head_title, t("users.settings.teams.head_title")) %>
+<% provide(:container_class, "no-second-nav-container") %>
+
+<% content_for :head do %>
+
+<% end %>
+
+
+ <%= render partial: 'users/settings/teams/header' %>
+
+ <%= link_to users_settings_team_user_groups_path, class: "hover:text-black text-black hover:no-underline" do %>
+
+ <% end %>
+
+ <%= t('user_groups.show.title', group: @user_group.name) %>
+
+
+
+
+
+
+<%= javascript_include_tag 'vue_user_groups_show' %>
diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb
index 03654ee2b..ead0f05ab 100644
--- a/config/initializers/extends.rb
+++ b/config/initializers/extends.rb
@@ -757,6 +757,7 @@ class Extends
teams/show
teams/members
user_groups/index
+ user_groups/show
)
DEFAULT_USER_NOTIFICATION_SETTINGS = {
diff --git a/config/locales/en.yml b/config/locales/en.yml
index c45ee4d10..d383a2744 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -4217,6 +4217,25 @@ en:
confirm: "Delete group"
toolbar:
delete: "Delete"
+ show:
+ title: "Group %{group}"
+ add_members: "Add members"
+ name: "Name"
+ email: "Email"
+ created_at: "Added on"
+ remove: "Remove"
+ add_members_modal:
+ title: "Add members"
+ select_members: "Select members"
+ select_members_placeholder: "Search for members"
+ success: "Members added successfully."
+ error: "There was a problem adding members. Please try again."
+ remove_modal:
+ title: "Remove member from %{group}"
+ description_html: "You are about to remove %{number} member(s) from this group.
Group-based access to content will be removed.
Are you sure you want to remove members from group?"
+ confirm: "Remove"
+ success: "Members removed successfully."
+ error: "There was a problem removing members. Please try again."
invite_users:
to_team:
title: "Invite members to %{team}"
diff --git a/config/routes.rb b/config/routes.rb
index 40fb76cc6..94f60b2fe 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -150,7 +150,12 @@ Rails.application.routes.draw do
resources :teams, only: [] do
resources :user_groups, only: %i(index create update destroy show) do
- resources :user_group_memberships, only: %i(index create update destroy)
+ resources :user_group_memberships, only: %i(index create update) do
+ collection do
+ delete :destroy_multiple
+ post :actions_toolbar
+ end
+ end
collection do
get :unassigned_users
post :actions_toolbar
diff --git a/config/webpack/webpack.config.js b/config/webpack/webpack.config.js
index 51cafc7e3..2ef9dc729 100644
--- a/config/webpack/webpack.config.js
+++ b/config/webpack/webpack.config.js
@@ -75,7 +75,8 @@ const entryList = {
vue_design_system_table: './app/javascript/packs/vue/design_system/table.js',
vue_favorites_widget: './app/javascript/packs/vue/favorites_widget.js',
vue_experiment_description_modal: './app/javascript/packs/vue/experiment_description_modal.js',
- vue_user_groups_table: './app/javascript/packs/vue/user_groups_table.js'
+ vue_user_groups_table: './app/javascript/packs/vue/user_groups_table.js',
+ vue_user_groups_show: './app/javascript/packs/vue/user_groups_show.js'
};
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949