Add new protocol modal [SCI-7810]

This commit is contained in:
Anton 2023-02-17 14:27:20 +01:00
parent ae607839c1
commit a95ca75362
13 changed files with 184 additions and 133 deletions

View file

@ -35,63 +35,6 @@ function initEditProtocolDescription() {
}); });
} }
function initCopyToRepository() {
var link = "[data-action='copy-to-repository']";
var modal = '#copy-to-repository-modal';
var modalBody = '.modal-body';
var submitBtn = ".modal-footer [data-action='submit']";
$('.my-modules-protocols-index')
.on('ajax:success', link, function(e, data) {
$(modal).find(modalBody).html(data.html);
$(modal).find(modalBody).find("[data-role='copy-to-repository']")
.on('ajax:success', function(e2, data2) {
if (data2.refresh !== null) {
// Reload page
location.reload();
} else {
// Simply hide the modal
$(modal).modal('hide');
}
})
.on('ajax:error', function(e2, data2) {
// Display errors in form
$(modal).find(submitBtn)[0].disabled = false;
if (data2.status === 422) {
$(this).renderFormErrors('protocol', data2.responseJSON);
} else {
// Simply display global error
alert(data2.responseJSON.message);
}
});
$(modal).modal('show');
$(modal).find(submitBtn)[0].disabled = false;
})
.on('ajax:error', function() {});
$(modal).on('click', submitBtn, function() {
// Submit the embedded form
$(modal).find(submitBtn)[0].disabled = true;
$(modal).find('form').submit();
});
$(modal).find(modalBody).on('click', "[data-role='link-check']", function() {
var text = $(this).closest('.modal-body').find("[data-role='link-text']");
if ($(this).prop('checked')) {
text.show();
} else {
text.hide();
}
});
$(modal).on('hidden.bs.modal', function() {
$(modal).find(modalBody).find("[data-role='copy-to-repository']")
.off('ajax:success ajax:error');
$(modal).find(modalBody).html('');
});
}
function initLinkUpdate() { function initLinkUpdate() {
var modal = $('#confirm-link-update-modal'); var modal = $('#confirm-link-update-modal');
var modalTitle = modal.find('.modal-title'); var modalTitle = modal.find('.modal-title');

View file

@ -0,0 +1,30 @@
/* global dropdownSelector HelperModule */
(function() {
$('#newProtocolModal').on('change', '#protocol_visibility', function() {
$('#roleSelectWrapper').toggleClass('hidden', !$(this)[0].checked);
});
let roleSelector = '#newProtocolModal #protocol_role_selector';
dropdownSelector.init(roleSelector, {
noEmptyOption: true,
singleSelect: true,
closeOnSelect: true,
selectAppearance: 'simple',
onChange: function() {
$('#protocol_default_public_user_role_id').val(dropdownSelector.getValues(roleSelector));
}
});
$('#newProtocolModal')
.on('ajax:error', 'form', function(e, error) {
let msg = error.responseJSON.error;
$('#newProtocolModal #protocol_name').parent().addClass('error').attr('data-error-text', msg);
})
.on('ajax:success', 'form', function(e, data) {
if (data.message) {
HelperModule.flashAlertMsg(data.message, 'success');
}
$('#newProtocolModal #protocol_name').parent().removeClass('error');
$('#newProtocolModal').modal('hide');
});
}());

View file

@ -233,3 +233,27 @@
width: 90vw; width: 90vw;
} }
} }
#newProtocolModal {
.description {
@include font-button;
margin: 1em 0;
}
.default-role-container {
@include font-button;
display: flex;
line-height: 24px;
margin: 1em 0;
.sci-checkbox-container {
margin: 4px 8px 4px 0;
}
small {
@include font-small;
display: block;
line-height: 16px;
}
}
}

View file

@ -256,29 +256,20 @@ class ProtocolsController < ApplicationController
end end
def create def create
@protocol = Protocol.new( @protocol = Protocol.new(create_params)
team: @current_team,
protocol_type: Protocol.protocol_types[:in_repository_draft],
added_by: current_user,
name: t('protocols.index.default_name')
)
ts = Time.now ts = Time.now
@protocol.team = current_team
@protocol.protocol_type = :in_repository_draft
@protocol.added_by = current_user
@protocol.record_timestamps = false @protocol.record_timestamps = false
@protocol.created_at = ts @protocol.created_at = ts
@protocol.updated_at = ts @protocol.updated_at = ts
@protocol.published_on = ts if @type == :public
rename_record(@protocol, :name)
if @protocol.save if @protocol.save
log_activity(:create_protocol_in_repository, nil, protocol: @protocol.id) log_activity(:create_protocol_in_repository, nil, protocol: @protocol.id)
TinyMceAsset.update_images(@protocol, params[:tiny_mce_images], current_user)
redirect_to protocol_path(@protocol) redirect_to protocol_path(@protocol)
else else
flash[:error] = @protocol.errors.full_messages.join(', ') render json: { error: @protocol.errors.full_messages.join(', ') }, status: :unprocessable_entity
redirect_to protocols_path
end end
end end
@ -334,18 +325,10 @@ class ProtocolsController < ApplicationController
end end
def copy_to_repository def copy_to_repository
link_protocols = params[:link] &&
can_manage_protocol_in_module?(@protocol) &&
can_create_protocols_in_repository?(@protocol.team)
respond_to do |format| respond_to do |format|
transaction_error = false transaction_error = false
Protocol.transaction do Protocol.transaction do
@new = @protocol.copy_to_repository( @new = @protocol.copy_to_repository(Protocol.new(create_params), current_user)
copy_to_repository_params[:name],
copy_to_repository_params[:protocol_type],
link_protocols,
current_user
)
rescue StandardError => e rescue StandardError => e
transaction_error = true transaction_error = true
Rails.logger.error(e.message) Rails.logger.error(e.message)
@ -353,23 +336,19 @@ class ProtocolsController < ApplicationController
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
if transaction_error format.json do
# Bad request error if transaction_error
format.json do # Bad request error
render json: { render json: {
message: t('my_modules.protocols.copy_to_repository_modal.error_400') message: t('my_modules.protocols.copy_to_repository_modal.error_400')
}, },
status: :bad_request status: :bad_request
elsif @new.invalid?
render json: { error: @new.errors.full_messages.join(', ') }, status: :unprocessable_entity
else
# Everything good, render 200
render json: { message: t('my_modules.protocols.copy_to_repository_modal.success_message') }
end end
elsif @new.invalid?
# Render errors
format.json do
render json: @new.errors,
status: :unprocessable_entity
end
else
# Everything good, render 200
format.json { render json: { refresh: link_protocols }, status: :ok }
end end
end end
end end
@ -1241,7 +1220,7 @@ class ProtocolsController < ApplicationController
end end
def check_copy_to_repository_permissions def check_copy_to_repository_permissions
@protocol = Protocol.find_by_id(params[:id]) @protocol = Protocol.find_by(id: params[:id])
@my_module = @protocol.my_module @my_module = @protocol.my_module
render_403 unless @my_module.present? && render_403 unless @my_module.present? &&
@ -1273,6 +1252,10 @@ class ProtocolsController < ApplicationController
params.require(:protocol).permit(:name, :protocol_type) params.require(:protocol).permit(:name, :protocol_type)
end end
def create_params
params.require(:protocol).permit(:name, :default_public_user_role_id, :visibility)
end
def check_protocolsio_import_permissions def check_protocolsio_import_permissions
render_403 unless can_create_protocols_in_repository?(current_team) render_403 unless can_create_protocols_in_repository?(current_team)
end end

View file

@ -31,9 +31,8 @@
</li> </li>
<li> <li>
<a <a
ref="saveProtocol" data-toggle="modal"
data-action="copy-to-repository" data-target="#newProtocolModal"
@click="saveProtocol"
:class="{ disabled: !protocol.attributes.urls.save_to_repo_url }" :class="{ disabled: !protocol.attributes.urls.save_to_repo_url }"
> >
<span class="fas fa-save"></span> <span class="fas fa-save"></span>
@ -132,7 +131,6 @@ export default {
mounted() { mounted() {
// Legacy global functions from app/assets/javascripts/my_modules/protocols.js // Legacy global functions from app/assets/javascripts/my_modules/protocols.js
initLoadFromRepository(); initLoadFromRepository();
initCopyToRepository();
initImport(); initImport();
initLinkUpdate(); initLinkUpdate();
}, },
@ -150,11 +148,6 @@ export default {
$(this.$refs.loadProtocol).trigger("ajax:success", data); $(this.$refs.loadProtocol).trigger("ajax:success", data);
}); });
}, },
saveProtocol() {
$.get(this.protocol.attributes.urls.save_to_repo_url).success((data) => {
$(this.$refs.saveProtocol).trigger("ajax:success", data);
});
},
unlinkProtocol() { unlinkProtocol() {
$.get(this.protocol.attributes.urls.unlink_url).success((data) => { $.get(this.protocol.attributes.urls.unlink_url).success((data) => {
$(this.$refs.unlinkProtocol).trigger("ajax:success", data); $(this.$refs.unlinkProtocol).trigger("ajax:success", data);

View file

@ -0,0 +1,28 @@
# frozen_string_literal: true
module UserAssignments
class ProtocolGroupAssignmentJob < ApplicationJob
queue_as :high_priority
def perform(team, protocols, assigned_by)
@team = team
@assigned_by = assigned_by
ActiveRecord::Base.transaction do
team.users.where.not(id: assigned_by).find_each do |user|
user_assignment = UserAssignment.find_or_initialize_by(
user: user,
assignable: protocols
)
next if user_assignment.manually_assigned?
user_assignment.update!(
user_role: protocol.default_public_user_role || UserRole.find_predefined_viewer_role,
assigned_by: @assigned_by
)
end
end
end
end
end

View file

@ -19,6 +19,7 @@ class Protocol < ApplicationRecord
after_update :update_user_assignments, if: -> { saved_change_to_protocol_type? && in_repository? } after_update :update_user_assignments, if: -> { saved_change_to_protocol_type? && in_repository? }
after_destroy :decrement_linked_children after_destroy :decrement_linked_children
after_save :update_linked_children after_save :update_linked_children
after_create :auto_assign_protocol_members, if: :visible?
skip_callback :create, :after, :create_users_assignments, if: -> { in_module? } skip_callback :create, :after, :create_users_assignments, if: -> { in_module? }
enum visibility: { hidden: 0, visible: 1 } enum visibility: { hidden: 0, visible: 1 }
@ -543,40 +544,16 @@ class Protocol < ApplicationRecord
save! save!
end end
def copy_to_repository(new_name, new_protocol_type, link_protocols, current_user) def copy_to_repository(clone, current_user)
clone = Protocol.new( clone.team = team
name: new_name, clone.protocol_type = :in_repository_draft
description: description, clone.added_by = current_user
protocol_type: new_protocol_type,
added_by: current_user,
team: team
)
clone.published_on = Time.now if clone.in_repository_public?
# Don't proceed further if clone is invalid # Don't proceed further if clone is invalid
return clone if clone.invalid? return clone if clone.invalid?
ActiveRecord::Base.no_touching do ActiveRecord::Base.no_touching do
# Okay, clone seems to be valid: let's clone it # Okay, clone seems to be valid: let's clone it
clone = deep_clone(clone, current_user) clone = deep_clone(clone, current_user)
# If the above operation went well, update published_on
# timestamp
clone.update(published_on: Time.zone.now) if clone.in_repository_public?
end
# Link protocols if neccesary
if link_protocols
reload
self.name = clone.name
self.record_timestamps = false
self.added_by = current_user
self.parent = clone
ts = clone.updated_at
self.parent_updated_at = ts
self.updated_at = ts
self.protocol_type = Protocol.protocol_types[:linked]
save!
end end
clone clone
@ -662,6 +639,14 @@ class Protocol < ApplicationRecord
private private
def auto_assign_protocol_members
UserAssignments::ProtocolGroupAssignmentJob.perform_now(
team,
self,
last_modified_by || created_by
)
end
def update_user_assignments def update_user_assignments
case visibility case visibility
when 'visible' when 'visible'

View file

@ -153,7 +153,7 @@
<%= render partial: "my_modules/protocols/load_from_repository_modal.html.erb" %> <%= render partial: "my_modules/protocols/load_from_repository_modal.html.erb" %>
<!-- Copy to repository protocol modal --> <!-- Copy to repository protocol modal -->
<%= render partial: "my_modules/protocols/copy_to_repository_modal.html.erb" %> <%= render partial: "protocols/index/new_protocol_modal.html.erb", locals: {type: 'copy'} %>
<!-- Import protocol elements --> <!-- Import protocol elements -->
<%= render partial: "protocols/import_export/import_elements.html.erb" %> <%= render partial: "protocols/import_export/import_elements.html.erb" %>
@ -182,6 +182,7 @@
<%= javascript_include_tag("my_modules/pwa_mobile_app") %> <%= javascript_include_tag("my_modules/pwa_mobile_app") %>
<%= javascript_pack_tag 'pdfjs/pdf_js' %> <%= javascript_pack_tag 'pdfjs/pdf_js' %>
<%= stylesheet_pack_tag 'pdfjs/pdf_js_styles' %> <%= stylesheet_pack_tag 'pdfjs/pdf_js_styles' %>
<%= javascript_include_tag "protocols/new_protocol" %>
<%= javascript_pack_tag 'vue/protocol' %> <%= javascript_pack_tag 'vue/protocol' %>

View file

@ -43,6 +43,7 @@
<%= render partial: "protocols/index/import_results_modal.html.erb" %> <%= render partial: "protocols/index/import_results_modal.html.erb" %>
<%= render partial: "protocols/index/linked_children_modal.html.erb" %> <%= render partial: "protocols/index/linked_children_modal.html.erb" %>
<%= render partial: "protocols/index/protocolsio_modal.html.erb" %> <%= render partial: "protocols/index/protocolsio_modal.html.erb" %>
<%= render partial: "protocols/index/new_protocol_modal.html.erb", locals: {type: 'new'} %>
<%= render partial: "protocols/import_export/import_elements.html.erb" %> <%= render partial: "protocols/import_export/import_elements.html.erb" %>
@ -54,5 +55,6 @@
<%= stylesheet_link_tag 'datatables' %> <%= stylesheet_link_tag 'datatables' %>
<%= javascript_include_tag "assets/wopi/create_wopi_file" %> <%= javascript_include_tag "assets/wopi/create_wopi_file" %>
<%= javascript_include_tag "protocols/index" %> <%= javascript_include_tag "protocols/index" %>
<%= javascript_include_tag "protocols/new_protocol" %>
<%= javascript_pack_tag 'pdfjs/pdf_js' %> <%= javascript_pack_tag 'pdfjs/pdf_js' %>
<%= stylesheet_pack_tag 'pdfjs/pdf_js_styles' %> <%= stylesheet_pack_tag 'pdfjs/pdf_js_styles' %>

View file

@ -1,9 +1,13 @@
<template id="protocolGeneralToolbar"> <template id="protocolGeneralToolbar">
<div class="left-general-toolbar"> <div class="left-general-toolbar">
<%= button_to protocols_path(type: @type), disabled: !can_create_protocols_in_repository?(@current_team), class: 'btn btn-primary only-active' do %> <button data-toggle="modal"
data-target="#newProtocolModal"
<%= 'disabled' if !can_create_protocols_in_repository?(@current_team) %>
class="btn btn-primary only-active"
>
<span class="fas fa-plus"></span> <span class="fas fa-plus"></span>
<span class="hidden-xs"><%= t("protocols.index.create_new") %></span> <span class="hidden-xs"><%= t("protocols.index.create_new") %></span>
<% end %> </button>
<div id="protocol-import-group" class="sci-btn-group only-active" role="group"> <div id="protocol-import-group" class="sci-btn-group only-active" role="group">
<button class="btn btn-light btn-open-file <%= 'disabled' unless can_create_protocols_in_repository?(@current_team) %>" <button class="btn btn-light btn-open-file <%= 'disabled' unless can_create_protocols_in_repository?(@current_team) %>"

View file

@ -0,0 +1,46 @@
<div class="modal" id="newProtocolModal" tabindex="-1" role="dialog">
<%= form_with model: @protocol || Protocol.new, url: type == 'new' ? protocols_path : copy_to_repository_protocol_path(@protocol), method: :post do |f| %>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t('general.close') %>"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">
<%= t("protocols.new_protocol_modal.title_#{type}") %>
</h4>
</div>
<div class="modal-body">
<div class="sci-input-container">
<%= f.label :name, t("protocols.new_protocol_modal.name_label") %>
<%= f.text_field :name, placeholder: t("protocols.new_protocol_modal.name_placeholder"), class: 'sci-input-field' %>
</div>
<% if type == 'copy' %>
<p class="description">
<%= t("protocols.new_protocol_modal.description") %>
</p>
<% end %>
<div class="default-role-container">
<div class="sci-checkbox-container">
<%= f.check_box :visibility, { class: 'sci-checkbox'},:visible, :hidden %>
<span class="sci-checkbox-label"></span>
</div>
<div class="default-role-description">
<%= t("protocols.new_protocol_modal.access_label") %>
</div>
</div>
<div class="hidden" id="roleSelectWrapper">
<div class="sci-input-container">
<%= f.label :default_public_user_role_id, t("protocols.new_protocol_modal.role_label") %>
<% default_role = UserRole.find_by(name: I18n.t('user_roles.predefined.viewer')).id %>
<%= f.hidden_field :default_public_user_role_id, value: default_role %>
<%= f.select :role_selector, options_for_select(user_roles_collection, default_role) %>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"><%= t('general.cancel') %></button>
<%= f.button t("protocols.new_protocol_modal.create_#{type}") , class: "btn btn-primary" %>
</div>
</div>
</div>
<% end %>
</div>

View file

@ -60,6 +60,7 @@ Rails.application.config.assets.precompile += %w(protocols/index.js)
Rails.application.config.assets.precompile += %w(protocols/protocolsio.js) Rails.application.config.assets.precompile += %w(protocols/protocolsio.js)
Rails.application.config.assets.precompile += %w(protocols/header.js) Rails.application.config.assets.precompile += %w(protocols/header.js)
Rails.application.config.assets.precompile += %w(protocols/steps.js) Rails.application.config.assets.precompile += %w(protocols/steps.js)
Rails.application.config.assets.precompile += %w(protocols/new_protocol.js)
Rails.application.config.assets.precompile += %w(protocols/edit.js) Rails.application.config.assets.precompile += %w(protocols/edit.js)
Rails.application.config.assets.precompile += %w(protocols/import_export/eln_table.js) Rails.application.config.assets.precompile += %w(protocols/import_export/eln_table.js)
Rails.application.config.assets.precompile += %w(protocols/import_export/import.js) Rails.application.config.assets.precompile += %w(protocols/import_export/import.js)

View file

@ -1127,6 +1127,7 @@ en:
link_label: "Link task to this protocol in templates" link_label: "Link task to this protocol in templates"
link_text: "<strong>Warning!</strong>&nbsp;This will unlink the currently linked protocol." link_text: "<strong>Warning!</strong>&nbsp;This will unlink the currently linked protocol."
error_400: "Due to unknown error, protocol could not be copied to templates." error_400: "Due to unknown error, protocol could not be copied to templates."
success_message: "New protocol template was successfully saved. You can see it in protocol templates."
confirm: "Save" confirm: "Save"
load_from_file_flash: "Successfully loaded the protocol from the file." load_from_file_flash: "Successfully loaded the protocol from the file."
load_from_file_error: "Failed to load the protocol from file." load_from_file_error: "Failed to load the protocol from file."
@ -2655,6 +2656,16 @@ en:
comment: "Revision comment" comment: "Revision comment"
comment_placeholder: "What's new in this version?" comment_placeholder: "What's new in this version?"
publish: "Publish" publish: "Publish"
new_protocol_modal:
title_new: "Create new protocol template"
title_copy: "Save as new protocol template"
name_label: "Protocol template name"
name_placeholder: "Enter a protocol template name…"
description: "You will create a copy of a protocol and save it to Protocol templates."
access_label: "Grant access to all team members"
role_label: "Default user role"
create_new: "Create"
create_copy: "Save"
header: header:
details: 'Details' details: 'Details'
versions: 'Versions' versions: 'Versions'