add fe logic for disable/enable fields

This commit is contained in:
zmagoD 2021-04-30 13:26:06 +02:00
parent 8a74546cdf
commit 11e5662c47
17 changed files with 173 additions and 49 deletions

View file

@ -0,0 +1,45 @@
(function() {
'use strict';
function initNewUserAssignmentFormListener() {
$(document).on('change', 'form#new-user-assignment-to-project-form', function() {
let values = [];
let count = 0;
let submitBtn = $(this).find('input[type="submit"]')
$(this).find('input:checked').each((_, el) => {
let select = $(el).closest('.row').find('select');
let selectValue = parseInt(select.val(), 10);
values.push(selectValue);
count += 1;
if (isNaN(selectValue)) {
select.closest('.form-group ')
.addClass('has-error');
} else {
select.closest('.form-group ')
.removeClass('has-error');
}
});
if (values.includes(NaN) || values.length === 0) {
submitBtn.attr('disabled', 'disabled');
} else {
submitBtn.attr('disabled', false);
}
switch (count) {
case 0:
submitBtn.val(submitBtn.data('label-default'));
break;
case 1:
submitBtn.val(submitBtn.data('label-singular'));
break;
default:
submitBtn.val(submitBtn.data('label-plural').replace('{num}', count));
}
});
}
$(document).one('turbolinks:load', initNewUserAssignmentFormListener);
})();

View file

@ -46,6 +46,7 @@
//= require shared/autosave_form
//= require shared/replace_form
//= require shared/remote_destroy
//= require shared/toggle_visibility
//= require activestorage
//= require global_activities/side_pane
//= require protocols/header
@ -55,6 +56,7 @@
//= require_directory ./repositories/validators
//= require_directory ./dashboard
//= require_directory ./sitewide
//= require_directory ./access_permissions
//= require sidebar
//= require turbolinks

View file

@ -1,15 +1,18 @@
(function () {
(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:success', 'a[data-action*="remote-destroy"]', function({ currentTarget, flash }) {
let target = currentTarget.getAttribute('data-target');
document.querySelector(target).remove();
if (flash) {
HelperModule.flashAlertMsg(flash, 'success');
}
});
$(document).on('ajax:error', 'a[data-action*="remote-destroy"]', function(_, data) {
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
})
});
}
$(document).one('turbolinks:load', initRemoteDestroyListeners);

View file

@ -1,9 +1,9 @@
(function () {
(function() {
'use strict';
function initReplaceFormListeners() {
$(document).on('ajax:success', 'form[data-action*="replace-form"]', function(_, {form}) {
let newForm = $(form)
$(document).on('ajax:success', 'form[data-action*="replace-form"]', function(_, { form, flash }) {
let newForm = $(form);
let target = this.getAttribute('data-target');
if (target) {
@ -11,8 +11,13 @@
} else {
$(this).replaceWith(newForm);
}
newForm.find('.selectpicker').selectpicker();
})
if (flash) {
HelperModule.flashAlertMsg(flash, 'success');
}
});
}
$(document).one('turbolinks:load', initReplaceFormListeners);

View file

@ -8,14 +8,18 @@
ev.stopPropagation();
ev.preventDefault();
$.get(element.getAttribute('href')).then(function({html}) {
let targetID = element.getAttribute('data-target')
let targetElement = $(element).closest(targetID)
let newContainer = $(html)
targetElement.replaceWith(newContainer)
$.get(element.getAttribute('href')).then(function({html, flash}) {
let targetID = element.getAttribute('data-target');
let targetElement = $(element).closest(targetID);
let newContainer = $(html);
targetElement.replaceWith(newContainer);
newContainer.find('.selectpicker').selectpicker();
})
})
if (flash) {
HelperModule.flashAlertMsg(flash, 'success');
}
});
});
}
$(document).one('turbolinks:load', initSwapRemoteContainerListeners);

View file

@ -0,0 +1,18 @@
(function () {
'use strict';
function initToggleVisibilityListeners() {
$(document).on('change', 'input[data-action="toggle-visibility"]', function({currentTarget}) {
let toggleId = currentTarget.getAttribute('data-target');
let element = document.getElementById(toggleId);
if (currentTarget.checked) {
element.classList.remove('hidden');
} else {
element.classList.add('hidden');
}
});
}
$(document).one('turbolinks:load', initToggleVisibilityListeners);
})();

View file

@ -45,11 +45,12 @@ module AccessPermissions
def create
@form = AccessPermissions::NewUserProjectForm.new(current_user, @project)
@form.resource_members = permitted_create_params
respond_to do |format|
if @form.save
@message = t('access_permissions.create.success', count: @form.resource_members.count)
format.json { render :edit }
else
@message = t('access_permissions.create.failure')
format.json { render :new }
end
end
@ -58,11 +59,18 @@ module AccessPermissions
def destroy
user = @project.users.find(params[:user_id])
project_member = ProjectMember.new(user, @project)
project_member.destroy
respond_to do |format|
format.json do
render json: { status: :ok }
if project_member.destroy
format.json do
render json: { flash: t('access_permissions.destroy.success', member_name: user.full_name) },
status: :ok
end
else
format.json do
render json: { flash: t('access_permissions.destroy.failure') },
status: :unprocessable_entity
end
end
end
end

View file

@ -6,4 +6,8 @@ module UserRolesHelper
@user_roles_collection ||= UserRole.all.pluck(:name, :id)
end
end
def new_user_roles_collection
[[t('user_assignment.select_role'), nil]] + user_roles_collection
end
end

View file

@ -40,7 +40,7 @@ class ProjectMember
def destroy
user_assignment = UserAssignment.find_by!(assignable: @project, user: @user)
user_project = UserProject.find_by!(project: @project, user: @user)
return false if last_project_owner?
ActiveRecord::Base.transaction do
user_assignment.destroy!
user_project.destroy!
@ -51,6 +51,10 @@ class ProjectMember
@assign = ActiveModel::Type::Boolean.new.cast(value)
end
def last_project_owner?
project_owners.count == 1 && user_role.owner?
end
private
def set_user_role
@ -72,4 +76,10 @@ class ProjectMember
errors.add(:user_role_id, :already_present)
end
end
def project_owners
@project_owners ||= @project.user_assignments
.includes(:user_role)
.where(user_roles: { name: 'Owner' })
end
end

View file

@ -83,6 +83,10 @@ class UserRole < ApplicationRecord
)
end
def owner?
self.name == 'Owner'
end
private
def prevent_update

View file

@ -1,7 +1,7 @@
<% # frozen_string_literal: true %>
<div class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-dialog modal-md" role="document">
<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">&times;</span></button>

View file

@ -4,13 +4,13 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">
<%= link_to resource_path, remote: true, class: 'pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
<%= link_to resource_path, remote: true, class: 'pull-left spacer', 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', target: '#user_assignments_modal' } }) do |f| %>
<%= form_with(model: form_object, url: create_path, method: :post, remote: true, html: { id: 'new-user-assignment-to-project-form', 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| %>
@ -19,7 +19,7 @@
<% end %>
</div>
<div class="modal-footer">
<%= f.submit t('.submit'), class: "btn btn-primary" %>
<%= f.submit t('.submit'), class: "btn btn-primary", disabled: true, data: { 'label-default' => t('.submit'), 'label-singular' => t('.submit_singular'), 'label-plural' => t('.submit_plural') }%>
</div>
<% end %>
</div>

View file

@ -1,6 +1,9 @@
<% # frozen_string_literal: true %>
<% project_member = ProjectMember.new(user, project) %>
<% item_id = "project-member-#{user.id}" %>
<%
project_member = ProjectMember.new(user, project)
item_id = dom_id(user, :project_member)
%>
<%= 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 %>
@ -15,20 +18,22 @@
<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 %>
<% unless user == current_user %>
<div class="col-xs-7">
<div class="row">
<div class="col-xs-7">
<%= 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 class="col-xs-5">
<% unless project_member.last_project_owner? %>
<%= 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 %>
<% end %>
</div>
</div>
</div>
</div>
<% end %>
</div>
<% end %>

View file

@ -1,13 +1,19 @@
<% # frozen_string_literal: true %>
<% project_member = f.object %>
<% user = project_member.user %>
<%
project_member = f.object
user = project_member.user
id = dom_id(user, :new_project_member)
%>
<div class="row">
<%= f.hidden_field :user_id, value: user.id, name:"access_permissions_new_user_project_form[resource_members][#{user.id}][user_id]" %>
<div class="col-xs-1 checkbox text-center">
<label>
<%= f.check_box :assign, name: "access_permissions_new_user_project_form[resource_members][#{user.id}][assign]" %>
<%= f.check_box :assign,
name: "access_permissions_new_user_project_form[resource_members][#{user.id}][assign]",
data: { action: 'toggle-visibility', target: id}
%>
</label>
</div>
<div class="col-xs-1">
@ -19,10 +25,7 @@
<span><%= current_assignee_name(user) %></span>
</div>
<div class="col-xs-4 form-group">
<% unless user == current_user %>
<%= f.select :user_role_id, options_for_select(user_roles_collection), {}, name: "access_permissions_new_user_project_form[resource_members][#{user.id}][user_role_id]", class: 'form-control selectpicker pull-right' %>
<% end %>
<div class="col-xs-4 form-group hidden" id="<%= id %>">
<%= f.select :user_role_id, options_for_select(new_user_roles_collection), {}, name: "access_permissions_new_user_project_form[resource_members][#{user.id}][user_role_id]", class: 'form-control selectpicker pull-right' %>
</div>
</div>
<hr />

View file

@ -24,3 +24,5 @@ modal_container = controller.render_to_string(
json.html modal_container
json.form modal_container
json.flash @message

View file

@ -1,7 +1,7 @@
<% # frozen_string_literal: true %>
<div class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-dialog modal-md" role="document">
<%= render 'access_permissions/partials/edit_assignments_content', project: project, new_resource_path: new_resource_path, update_path: update_path %>
</div>
</div>

View file

@ -2317,7 +2317,16 @@ en:
user_assignment:
current_assignee: "(you)"
select_role: "Select role"
access_permissions:
create:
success:
one: "You have successfully granted access to %{count} member to the project."
other: "You have successfully granted access to %{count} members to the project."
failure: "Something went wrong"
destroy:
success: "You have successfully removed %{member_name} from the project."
failure: "Something went wrong"
partials:
edit_assignments_content:
title: "Manage access for to %{resource_name}"
@ -2325,6 +2334,8 @@ en:
new_assignments_form:
title: "Select members"
submit: "Grand access"
submit_singular: "Grand access to 1 user"
submit_plural: "Grand access to {num} users"
modals:
show_modal:
title: "Access to %{resource_name}"