mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-08 22:24:23 +08:00
fix the logic for addin/editing new project members
This commit is contained in:
parent
c136d953d0
commit
8a74546cdf
20 changed files with 202 additions and 92 deletions
|
@ -45,6 +45,7 @@
|
|||
//= require shared/swap_remote_container
|
||||
//= require shared/autosave_form
|
||||
//= require shared/replace_form
|
||||
//= require shared/remote_destroy
|
||||
//= require activestorage
|
||||
//= require global_activities/side_pane
|
||||
//= require protocols/header
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
'use strict';
|
||||
|
||||
function initAutosaveListeners() {
|
||||
$(document).on('change', 'form[data-action="autosave-form"]', function({ currentTarget }) {
|
||||
currentTarget.submit()
|
||||
$(document).on('change', 'form[data-action*="autosave-form"]', function({ currentTarget }) {
|
||||
$.rails.fire($(currentTarget), 'submit')
|
||||
})
|
||||
}
|
||||
|
||||
|
|
16
app/assets/javascripts/shared/remote_destroy.js
Normal file
16
app/assets/javascripts/shared/remote_destroy.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
function initRemoteDestroyListeners() {
|
||||
$(document).on('ajax:success', 'a[data-action*="remote-destroy"]', function({ currentTarget }) {
|
||||
let target = currentTarget.getAttribute('data-target')
|
||||
document.querySelector(target).remove()
|
||||
})
|
||||
|
||||
$(document).on('ajax:error', 'a[data-action*="remote-destroy"]', function(_, data) {
|
||||
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
|
||||
})
|
||||
}
|
||||
|
||||
$(document).one('turbolinks:load', initRemoteDestroyListeners);
|
||||
})();
|
|
@ -2,9 +2,15 @@
|
|||
'use strict';
|
||||
|
||||
function initReplaceFormListeners() {
|
||||
$(document).on('ajax:success', 'form[data-action="replace-form"]', function({ form }) {
|
||||
$(document).on('ajax:success', 'form[data-action*="replace-form"]', function(_, {form}) {
|
||||
let newForm = $(form)
|
||||
$(this).replaceWith(newForm);
|
||||
let target = this.getAttribute('data-target');
|
||||
|
||||
if (target) {
|
||||
$(target).replaceWith(newForm);
|
||||
} else {
|
||||
$(this).replaceWith(newForm);
|
||||
}
|
||||
newForm.find('.selectpicker').selectpicker();
|
||||
})
|
||||
}
|
||||
|
|
|
@ -32,13 +32,12 @@ module AccessPermissions
|
|||
end
|
||||
|
||||
def update
|
||||
@form = AccessPermissions::EditUserProjectForm.new(current_user, @project)
|
||||
@form.update(permitted_update_params)
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
if @project.update(permitted_update_params)
|
||||
head :no_content
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
render :project_member
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -47,18 +46,32 @@ module AccessPermissions
|
|||
@form = AccessPermissions::NewUserProjectForm.new(current_user, @project)
|
||||
@form.resource_members = permitted_create_params
|
||||
|
||||
flash[:notice] = "Success" if @form.save
|
||||
respond_to do |format|
|
||||
if @form.save
|
||||
format.json { render :edit }
|
||||
else
|
||||
format.json { render :new }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user = @project.users.find(params[:user_id])
|
||||
project_member = ProjectMember.new(user, @project)
|
||||
project_member.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.json :new
|
||||
format.json do
|
||||
render json: { status: :ok }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def permitted_update_params
|
||||
params.require(:project)
|
||||
.permit(user_assignments_attributes: %i[user_role_id _destroy id])
|
||||
params.require(:project_member)
|
||||
.permit(%i[user_role_id user_id])
|
||||
end
|
||||
|
||||
def permitted_create_params
|
||||
|
@ -67,7 +80,7 @@ module AccessPermissions
|
|||
end
|
||||
|
||||
def set_project
|
||||
@project = Project.includes(user_assignments: [:user, :user_role]).find_by(id: params[:id])
|
||||
@project = current_team.projects.includes(user_assignments: [:user, :user_role]).find_by(id: params[:id])
|
||||
|
||||
render_404 unless @project
|
||||
end
|
||||
|
|
23
app/forms/access_permissions/edit_user_project_form.rb
Normal file
23
app/forms/access_permissions/edit_user_project_form.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AccessPermissions
|
||||
class EditUserProjectForm
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :user, :project
|
||||
|
||||
def initialize(current_user, project)
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
def update(params)
|
||||
@user = project.users.find(params[:user_id])
|
||||
project_member = ProjectMember.new(user, project)
|
||||
project_member.user_role_id = params[:user_role_id]
|
||||
|
||||
project_member.update
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -19,7 +19,7 @@ module AccessPermissions
|
|||
if @error
|
||||
false
|
||||
else
|
||||
@resource_members.map(&:save)
|
||||
@resource_members.map(&:create)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
@ -31,7 +31,6 @@ module AccessPermissions
|
|||
project_member = ProjectMember.new(user, @project, current_user)
|
||||
project_member.assign = resource_member[:assign]
|
||||
project_member.user_role_id = resource_member[:user_role_id]
|
||||
byebug
|
||||
@error = true unless project_member.valid?
|
||||
|
||||
@resource_members << project_member
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module UserRolesHelper
|
||||
def user_roles_collection
|
||||
Rails.cache.fetch([current_user, 'available_user_roles']) do
|
||||
@user_roles_collection ||= [[t('user_assignment.select.default_option'), nil]] + UserRole.all.pluck(:name, :id)
|
||||
@user_roles_collection ||= UserRole.all.pluck(:name, :id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -371,7 +371,7 @@ class Project < ApplicationRecord
|
|||
UserAssignment.create(
|
||||
user: created_by,
|
||||
assignable: self,
|
||||
user_role: UserRole.owner_role
|
||||
user_role: UserRole.find_by(name: 'Owner')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,20 +4,24 @@ class ProjectMember
|
|||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :user, :project, :assign, :user_role_id, :user_id
|
||||
attr_reader :current_user
|
||||
attr_reader :current_user, :user_assignment, :user_role
|
||||
|
||||
validates :user, :project, :user_role_id, presence: true
|
||||
validate :role_presence
|
||||
validate :validate_user_project_relation_presence
|
||||
validate :validate_user_project_assignment_presence
|
||||
delegate :user_role, to: :user_assignment, allow_nil: true
|
||||
|
||||
def initialize(user, project, current_user)
|
||||
validates :user, :project, presence: true, if: -> { assign }
|
||||
validates :user_role_id, presence: true, if: -> { assign }
|
||||
validate :validate_role_presence, if: -> { assign }
|
||||
validate :validate_user_project_relation_presence, if: -> { assign }
|
||||
validate :validate_user_project_assignment_presence, if: -> { assign }
|
||||
|
||||
def initialize(user, project, current_user = nil)
|
||||
@user = user
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
@user_assignment = UserAssignment.find_by(assignable: @project, user: @user)
|
||||
end
|
||||
|
||||
def save
|
||||
def create
|
||||
return unless assign
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
|
@ -26,6 +30,23 @@ class ProjectMember
|
|||
end
|
||||
end
|
||||
|
||||
def update
|
||||
validate_role_presence
|
||||
return false unless valid?
|
||||
user_assignment = UserAssignment.find_by!(assignable: @project, user: @user)
|
||||
user_assignment.update(user_role: set_user_role)
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_assignment = UserAssignment.find_by!(assignable: @project, user: @user)
|
||||
user_project = UserProject.find_by!(project: @project, user: @user)
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
user_assignment.destroy!
|
||||
user_project.destroy!
|
||||
end
|
||||
end
|
||||
|
||||
def assign=(value)
|
||||
@assign = ActiveModel::Type::Boolean.new.cast(value)
|
||||
end
|
||||
|
@ -33,11 +54,11 @@ class ProjectMember
|
|||
private
|
||||
|
||||
def set_user_role
|
||||
UserRole.find!(user_role_id)
|
||||
UserRole.find(user_role_id)
|
||||
end
|
||||
|
||||
def role_presence
|
||||
errors.add(:user_role_id) if UserRole.find(user_role_id).nil?
|
||||
def validate_role_presence
|
||||
errors.add(:user_role_id, :not_found) if UserRole.find_by(id: user_role_id).nil?
|
||||
end
|
||||
|
||||
def validate_user_project_relation_presence
|
||||
|
@ -48,7 +69,7 @@ class ProjectMember
|
|||
|
||||
def validate_user_project_assignment_presence
|
||||
if UserAssignment.find_by(assignable: @project, user: @user).present?
|
||||
errors.add(:user_role_id)
|
||||
errors.add(:user_role_id, :already_present)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UserProject < ApplicationRecord
|
||||
# TODO: Remove this from DB (Ask Alex)
|
||||
enum role: { owner: 0, normal_user: 1, technician: 2, viewer: 3 }
|
||||
|
||||
validates :role, presence: true
|
||||
validates :user, presence: true, uniqueness: { scope: :project }
|
||||
validates :project, presence: true
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<div class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="user_assignments_modal">
|
||||
<%= form_with(model: resource, url: update_path, remote: true, html: { data: { action: 'autosave-form' } }) do |f| %>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><%= t '.title', resource_name: resource.name %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%= f.nested_fields_for :user_assignments, resource.user_assignments, wrapper_options: { class: 'row' } do |user_assignment_form| %>
|
||||
<%= render('access_permissions/partials/user_permission_field.html.erb', f: user_assignment_form) if user_assignment_form.object.persisted? %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%= link_to new_resource_path, class: 'btn btn-default pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
|
||||
<i class="fas fa-plus"></i>
|
||||
<%= t '.new_resource_assignments', resource: resource.model_name.human.downcase %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,19 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<div class="modal-content" id="user_assignments_modal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><%= t '.title', resource_name: project.name %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<% project.users.each do |user| %>
|
||||
<%= render('access_permissions/partials/project_member_field.html.erb', user: user, project: project, update_path: update_path) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%= link_to new_resource_path, class: 'btn btn-default pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
|
||||
<i class="fas fa-plus"></i>
|
||||
<%= t '.new_resource_assignments', resource: project.model_name.human.downcase %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
|
@ -4,13 +4,13 @@
|
|||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<%= link_to resource_path, class: 'pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
|
||||
<%= link_to resource_path, remote: true, class: 'pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
<% end %>
|
||||
<%= t '.title', resource_name: resource.name %>
|
||||
</h4>
|
||||
</div>
|
||||
<%= form_with(model: form_object, url: create_path, method: :post, remote: true, html: { data: { action: 'replace-form' } }) do |f| %>
|
||||
<%= form_with(model: form_object, url: create_path, method: :post, remote: true, html: { data: { action: 'replace-form', target: '#user_assignments_modal' } }) do |f| %>
|
||||
<div class="modal-body">
|
||||
<% f.object.resource_members.each do |member| %>
|
||||
<%= f.fields_for :resource_members, member do |member_form| %>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
<% project_member = ProjectMember.new(user, project) %>
|
||||
<% item_id = "project-member-#{user.id}" %>
|
||||
|
||||
<%= form_with(model: project_member, url: update_path, method: :put, remote: true, html: { id: item_id, data: { action: 'replace-form autosave-form' } }) do |f| %>
|
||||
<%= f.hidden_field :user_id, value: f.object.user.id %>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<span class="global-avatar-container">
|
||||
<%= image_tag avatar_path(user, :icon_small), title: current_assignee_name(user), class: 'img-circle pull-left' %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<span><%= current_assignee_name(user) %></span>
|
||||
<br>
|
||||
<small class="text-muted"><%= project_member.user_role.name %></small>
|
||||
</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<% unless user == current_user %>
|
||||
<%= f.select :user_role_id, options_for_select(user_roles_collection, selected: f.object.user_role.id), {}, class: 'form-control selectpicker' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<%= link_to access_permissions_project_path(project, user_id: user, format: :json), remote: true, method: :delete, class: 'btn btn-link', data: { action: 'remote-destroy', target: "##{item_id}" } do %>
|
||||
<span class="fas fa-times"></span>
|
||||
<%= t 'general.remove' %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
|
@ -1,28 +0,0 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
<% user_assignment = f.object %>
|
||||
|
||||
<div class="col-xs-2">
|
||||
<span class="global-avatar-container">
|
||||
<%= image_tag avatar_path(user_assignment.user, :icon_small), title: current_assignee_name(user_assignment.user), class: 'img-circle pull-left' %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<span><%= current_assignee_name(user_assignment.user) %></span>
|
||||
<br>
|
||||
<small class="text-muted"><%= user_assignment.user_role.name %></small>
|
||||
</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<% unless user_assignment.user == current_user %>
|
||||
<%= f.select :user_role_id, options_for_select(user_roles_collection), {}, class: 'form-control selectpicker' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<%= f.remove_nested_fields_link class: 'btn btn-link' do %>
|
||||
<span class="fas fa-times"></span>
|
||||
<%= t 'general.remove' %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,12 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.modal controller.render_to_string(
|
||||
partial: 'access_permissions/modals/edit_modal',
|
||||
partial: 'access_permissions/projects/modals/edit_modal',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
resource: @project,
|
||||
project: @project,
|
||||
update_path: access_permissions_project_path(@project, format: :json),
|
||||
new_resource_path: new_access_permissions_project_path(id: @project, format: :json)
|
||||
},
|
||||
layout: false
|
||||
)
|
||||
|
||||
modal_container = controller.render_to_string(
|
||||
partial: 'access_permissions/partials/edit_assignments_content',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
project: @project,
|
||||
update_path: access_permissions_project_path(@project, format: :json),
|
||||
new_resource_path: new_access_permissions_project_path(id: @project, format: :json)
|
||||
},
|
||||
layout: false
|
||||
)
|
||||
|
||||
json.html modal_container
|
||||
json.form modal_container
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<% # frozen_string_literal: true %>
|
||||
|
||||
<div class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<%= render 'access_permissions/partials/edit_assignments_content', project: project, new_resource_path: new_resource_path, update_path: update_path %>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.form controller.render_to_string(
|
||||
partial: 'access_permissions/partials/project_member_field',
|
||||
formats: [:html],
|
||||
locals: {
|
||||
user: @form.user,
|
||||
project: @project,
|
||||
update_path: access_permissions_project_path(@project, format: :json)
|
||||
},
|
||||
layout: false
|
||||
)
|
|
@ -2316,18 +2316,16 @@ en:
|
|||
default_task_name: "New Task"
|
||||
|
||||
user_assignment:
|
||||
select:
|
||||
default_option: "Select User role"
|
||||
current_assignee: "(you)"
|
||||
access_permissions:
|
||||
partials:
|
||||
edit_assignments_content:
|
||||
title: "Manage access for to %{resource_name}"
|
||||
new_resource_assignments: "Grant new access to %{resource}"
|
||||
new_assignments_form:
|
||||
title: "Select members"
|
||||
submit: "Grand access"
|
||||
modals:
|
||||
edit_modal:
|
||||
title: "Manage access for to %{resource_name}"
|
||||
new_resource_assignments: "Grant new access to %{resource}"
|
||||
show_modal:
|
||||
title: "Access to %{resource_name}"
|
||||
new_resource_assignments: "Grant new access to %{resource}"
|
||||
|
|
Loading…
Add table
Reference in a new issue