mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 23:16:15 +08:00
Merge pull request #2735 from urbanrotnik/ur-sci-4824-status-button
New task flow buttons and actions [SCI-4824] [SCI-4833]
This commit is contained in:
commit
957ac92669
|
@ -1,4 +1,4 @@
|
|||
/* global I18n dropdownSelector */
|
||||
/* global I18n dropdownSelector HelperModule animateSpinner */
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
function initTaskCollapseState() {
|
||||
|
@ -226,34 +226,33 @@ function bindEditTagsAjax() {
|
|||
});
|
||||
}
|
||||
|
||||
// Sets callback for completing/uncompleting task
|
||||
function applyTaskCompletedCallBack() {
|
||||
$("[data-action='complete-task'], [data-action='uncomplete-task']")
|
||||
.on('click', function() {
|
||||
var button = $(this);
|
||||
$.ajax({
|
||||
url: button.data('link-url'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (data.completed === true) {
|
||||
button.attr('data-action', 'uncomplete-task');
|
||||
button.find('.btn')
|
||||
.removeClass('btn-primary').addClass('btn-default');
|
||||
} else {
|
||||
button.attr('data-action', 'complete-task');
|
||||
button.find('.btn')
|
||||
.removeClass('btn-default').addClass('btn-primary');
|
||||
}
|
||||
$('#dueDateContainer').html(data.module_header_due_date);
|
||||
initDueDatePicker();
|
||||
$('.task-state-label').html(data.module_state_label);
|
||||
button.find('button').replaceWith(data.new_btn);
|
||||
},
|
||||
error: function() {
|
||||
function applyTaskStatusChangeCallBack() {
|
||||
$('.task-flows').on('click', '#dropdownTaskFlowList > li[data-state-id]', function() {
|
||||
var list = $('#dropdownTaskFlowList');
|
||||
var item = $(this);
|
||||
var container = list.closest('.task-flows');
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: list.data('link-url'),
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
data: { my_module: { status_id: item.data('state-id') } },
|
||||
success: function(data) {
|
||||
container.html(data.content);
|
||||
animateSpinner(null, false);
|
||||
},
|
||||
error: function(e) {
|
||||
animateSpinner(null, false);
|
||||
if (e.status === 403) {
|
||||
HelperModule.flashAlertMsg(I18n.t('my_module_statuses.update_status.error.no_permission'), 'danger');
|
||||
} else if (e.status === 422) {
|
||||
HelperModule.flashAlertMsg(e.errors, 'danger');
|
||||
} else {
|
||||
HelperModule.flashAlertMsg('error', 'danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initTagsSelector() {
|
||||
|
@ -380,7 +379,7 @@ function initAssignedUsersSelector() {
|
|||
}
|
||||
|
||||
initTaskCollapseState();
|
||||
applyTaskCompletedCallBack();
|
||||
applyTaskStatusChangeCallBack();
|
||||
initTagsSelector();
|
||||
bindEditTagsAjax();
|
||||
initStartDatePicker();
|
||||
|
|
|
@ -499,6 +499,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
.task-information {
|
||||
column-gap: 1em;
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
|
||||
.task-section-header {
|
||||
grid-column: 1 / span 1;
|
||||
}
|
||||
|
||||
.task-details {
|
||||
grid-column: 1 / span 1;
|
||||
grid-row: 2 / span 1;
|
||||
}
|
||||
|
||||
.task-flows {
|
||||
grid-column: 2 / span 1;
|
||||
grid-row: 1 / span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.my-module-protocol-status {
|
||||
.status-info-dropdown {
|
||||
|
@ -514,4 +534,18 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-information {
|
||||
grid-template-columns: auto;
|
||||
row-gap: .5em;
|
||||
|
||||
.task-details {
|
||||
grid-row: 3 / span 1;
|
||||
}
|
||||
|
||||
.task-flows {
|
||||
grid-column: unset;
|
||||
grid-row: 2 / span 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
25
app/assets/stylesheets/my_modules/status_flow.scss
Normal file
25
app/assets/stylesheets/my_modules/status_flow.scss
Normal file
|
@ -0,0 +1,25 @@
|
|||
// scss-lint:disable SelectorDepth
|
||||
// scss-lint:disable NestingDepth
|
||||
// scss-lint:disable SelectorFormat
|
||||
// scss-lint:disable ImportantRule
|
||||
|
||||
@import "constants";
|
||||
@import "mixins";
|
||||
|
||||
.content-pane.my-modules-protocols-index {
|
||||
.status-flow-dropdown {
|
||||
.dropdown-toggle {
|
||||
color: $color-white;
|
||||
text-align: left;
|
||||
width: 15em;
|
||||
|
||||
.caret {
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu > li {
|
||||
line-height: 35px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
border-color: $brand-focus;
|
||||
|
||||
.caret {
|
||||
transform: rotateX(180deg)
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class MyModulesController < ApplicationController
|
|||
before_action :check_manage_permissions, only: %i(description due_date update_description update_protocol_description)
|
||||
before_action :check_view_permissions, except: %i(update update_description update_protocol_description
|
||||
toggle_task_state)
|
||||
before_action :check_complete_module_permission, only: :complete_my_module
|
||||
before_action :check_update_state_permissions, only: :update_state
|
||||
before_action :set_inline_name_editing, only: %i(protocols results activities archive)
|
||||
|
||||
layout 'fluid'.freeze
|
||||
|
@ -249,109 +249,24 @@ class MyModulesController < ApplicationController
|
|||
|
||||
def archive
|
||||
@archived_results = @my_module.archived_results
|
||||
current_team_switch(@my_module
|
||||
.experiment
|
||||
.project
|
||||
.team)
|
||||
current_team_switch(@my_module.experiment.project.team)
|
||||
end
|
||||
|
||||
# Complete/uncomplete task
|
||||
def toggle_task_state
|
||||
respond_to do |format|
|
||||
if can_complete_module?(@my_module)
|
||||
@my_module.completed? ? @my_module.uncomplete : @my_module.complete
|
||||
completed = @my_module.completed?
|
||||
if @my_module.save
|
||||
task_completion_activity
|
||||
def update_state
|
||||
new_status = @my_module.my_module_status_flow.my_module_statuses.find_by(id: update_status_params[:status_id])
|
||||
return render_404 unless new_status
|
||||
|
||||
# Render new button HTML
|
||||
if completed
|
||||
new_btn_partial = 'my_modules/state_button_uncomplete.html.erb'
|
||||
else
|
||||
new_btn_partial = 'my_modules/state_button_complete.html.erb'
|
||||
end
|
||||
@my_module.update(my_module_status: new_status)
|
||||
|
||||
format.json do
|
||||
render json: {
|
||||
new_btn: render_to_string(partial: new_btn_partial),
|
||||
completed: completed,
|
||||
module_header_due_date: render_to_string(
|
||||
partial: 'my_modules/module_header_due_date.html.erb',
|
||||
locals: { my_module: @my_module }
|
||||
),
|
||||
module_state_label: render_to_string(
|
||||
partial: 'my_modules/module_state_label.html.erb',
|
||||
locals: { my_module: @my_module }
|
||||
)
|
||||
}
|
||||
end
|
||||
else
|
||||
format.json { render json: {}, status: :unprocessable_entity }
|
||||
end
|
||||
else
|
||||
format.json { render json: {}, status: :unauthorized }
|
||||
end
|
||||
end
|
||||
end
|
||||
render json: { content: render_to_string(
|
||||
partial: 'my_modules/status_flow/task_flow_button.html.erb',
|
||||
locals: { my_module: @my_module })
|
||||
}, status: :ok
|
||||
|
||||
def complete_my_module
|
||||
respond_to do |format|
|
||||
if @my_module.uncompleted? && @my_module.check_completness_status
|
||||
@my_module.complete
|
||||
@my_module.save
|
||||
task_completion_activity
|
||||
format.json do
|
||||
render json: {
|
||||
task_button_title: t('my_modules.buttons.uncomplete'),
|
||||
module_header_due_date: render_to_string(
|
||||
partial: 'my_modules/module_header_due_date.html.erb',
|
||||
locals: { my_module: @my_module }
|
||||
),
|
||||
module_state_label: render_to_string(
|
||||
partial: 'my_modules/module_state_label.html.erb',
|
||||
locals: { my_module: @my_module }
|
||||
)
|
||||
}, status: :ok
|
||||
end
|
||||
else
|
||||
format.json { render json: {}, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def task_completion_activity
|
||||
completed = @my_module.completed?
|
||||
log_activity(completed ? :complete_task : :uncomplete_task)
|
||||
start_work_on_next_task_notification
|
||||
end
|
||||
|
||||
def start_work_on_next_task_notification
|
||||
if @my_module.completed?
|
||||
title = t('notifications.start_work_on_next_task',
|
||||
user: current_user.full_name,
|
||||
module: @my_module.name)
|
||||
message = t('notifications.start_work_on_next_task_message',
|
||||
project: link_to(@project.name, project_url(@project)),
|
||||
experiment: link_to(@experiment.name,
|
||||
canvas_experiment_url(@experiment)),
|
||||
my_module: link_to(@my_module.name,
|
||||
protocols_my_module_url(@my_module)))
|
||||
notification = Notification.create(
|
||||
type_of: :recent_changes,
|
||||
title: sanitize_input(title, %w(strong a)),
|
||||
message: sanitize_input(message, %w(strong a)),
|
||||
generator_user_id: current_user.id
|
||||
)
|
||||
# create notification for all users on the next modules in the workflow
|
||||
@my_module.my_modules.map(&:users).flatten.uniq.each do |target_user|
|
||||
next if target_user == current_user || !target_user.recent_notification
|
||||
UserNotification.create(notification: notification, user: target_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load_vars
|
||||
@my_module = MyModule.find_by_id(params[:id])
|
||||
if @my_module
|
||||
|
@ -384,8 +299,9 @@ class MyModulesController < ApplicationController
|
|||
render_403 unless can_read_experiment?(@my_module.experiment)
|
||||
end
|
||||
|
||||
def check_complete_module_permission
|
||||
render_403 unless can_complete_module?(@my_module)
|
||||
def check_update_state_permissions
|
||||
return render_403 unless can_change_my_module_flow_status?(@my_module)
|
||||
render_404 unless @my_module.my_module_status
|
||||
end
|
||||
|
||||
def set_inline_name_editing
|
||||
|
@ -414,6 +330,10 @@ class MyModulesController < ApplicationController
|
|||
update_params
|
||||
end
|
||||
|
||||
def update_status_params
|
||||
params.require(:my_module).permit(:status_id)
|
||||
end
|
||||
|
||||
def log_start_date_change_activity(start_date_changes)
|
||||
type_of = if start_date_changes[0].nil? # set started_on
|
||||
message_items = { my_module_started_on: @my_module.started_on }
|
||||
|
|
|
@ -22,6 +22,14 @@ class MyModuleStatus < ApplicationRecord
|
|||
validate :next_in_same_flow, if: -> { next_status.present? }
|
||||
validate :previous_in_same_flow, if: -> { previous_status.present? }
|
||||
|
||||
def initial_status?
|
||||
my_module_status_flow.initial_status == self
|
||||
end
|
||||
|
||||
def final_status?
|
||||
my_module_status_flow.final_status == self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def next_in_same_flow
|
||||
|
|
|
@ -19,6 +19,6 @@ class MyModuleStatusFlow < ApplicationRecord
|
|||
end
|
||||
|
||||
def final_status
|
||||
my_module_statuses.find_by(next_status: nil)
|
||||
my_module_statuses.left_outer_joins(:next_status).find_by('next_statuses_my_module_statuses.id': nil)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,85 +60,6 @@ Canaid::Permissions.register_for(Experiment) do
|
|||
end
|
||||
end
|
||||
|
||||
Canaid::Permissions.register_for(MyModule) do
|
||||
# Module, its experiment and its project must be active for all the specified
|
||||
# permissions
|
||||
# Also checking status implications
|
||||
%i(manage_module
|
||||
manage_users_in_module
|
||||
assign_repository_rows_to_module
|
||||
assign_sample_to_module
|
||||
complete_module
|
||||
create_comments_in_module
|
||||
create_my_module_repository_snapshot
|
||||
manage_my_module_repository_snapshots)
|
||||
.each do |perm|
|
||||
can perm do |_, my_module|
|
||||
my_module.active? &&
|
||||
my_module.experiment.active? &&
|
||||
my_module.experiment.project.active? &&
|
||||
(if my_module.my_module_status
|
||||
my_module.my_module_status&.my_module_status_implications&.all? { |implication| implication.call(my_module) }
|
||||
else
|
||||
true
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
# module: update, archive, move
|
||||
# result: create, update
|
||||
can :manage_module do |user, my_module|
|
||||
can_manage_experiment?(user, my_module.experiment)
|
||||
end
|
||||
|
||||
# NOTE: Must not be dependent on canaid parmision for which we check if it's
|
||||
# active
|
||||
# module: restore
|
||||
can :restore_module do |user, my_module|
|
||||
user.is_user_or_higher_of_project?(my_module.experiment.project) &&
|
||||
my_module.archived?
|
||||
end
|
||||
|
||||
# module: assign/reassign/unassign users
|
||||
can :manage_users_in_module do |user, my_module|
|
||||
user.is_owner_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: assign/unassign repository record
|
||||
# NOTE: Use 'module_page? &&' before calling this permission!
|
||||
can :assign_repository_rows_to_module do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: assign/unassign sample
|
||||
# NOTE: Use 'module_page? &&' before calling this permission!
|
||||
can :assign_sample_to_module do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: complete/uncomplete
|
||||
can :complete_module do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: create comment
|
||||
# result: create comment
|
||||
# step: create comment
|
||||
can :create_comments_in_module do |user, my_module|
|
||||
can_create_comments_in_project?(user, my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: create a snapshot of repository item
|
||||
can :create_my_module_repository_snapshot do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: make a repository snapshot selected
|
||||
can :manage_my_module_repository_snapshots do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
end
|
||||
|
||||
Canaid::Permissions.register_for(Protocol) do
|
||||
# Protocol needs to be in a module for all Protocol permissions below
|
||||
# experiment level
|
||||
|
@ -180,7 +101,7 @@ Canaid::Permissions.register_for(Protocol) do
|
|||
|
||||
# step: complete/uncomplete
|
||||
can :complete_or_checkbox_step do |user, protocol|
|
||||
can_complete_module?(user, protocol.my_module)
|
||||
can_change_my_module_flow_status?(user, protocol.my_module)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
72
app/permissions/my_module.rb
Normal file
72
app/permissions/my_module.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
Canaid::Permissions.register_for(MyModule) do
|
||||
# Module, its experiment and its project must be active for all the specified
|
||||
# permissions
|
||||
%i(manage_module
|
||||
manage_users_in_module
|
||||
assign_repository_rows_to_module
|
||||
assign_sample_to_module
|
||||
change_my_module_flow_status
|
||||
create_comments_in_module
|
||||
create_my_module_repository_snapshot
|
||||
manage_my_module_repository_snapshots)
|
||||
.each do |perm|
|
||||
can perm do |_, my_module|
|
||||
my_module.active? &&
|
||||
my_module.experiment.active? &&
|
||||
my_module.experiment.project.active?
|
||||
end
|
||||
end
|
||||
|
||||
# module: update, archive, move
|
||||
# result: create, update
|
||||
can :manage_module do |user, my_module|
|
||||
can_manage_experiment?(user, my_module.experiment)
|
||||
end
|
||||
|
||||
# NOTE: Must not be dependent on canaid parmision for which we check if it's
|
||||
# active
|
||||
# module: restore
|
||||
can :restore_module do |user, my_module|
|
||||
user.is_user_or_higher_of_project?(my_module.experiment.project) &&
|
||||
my_module.archived?
|
||||
end
|
||||
|
||||
# module: assign/reassign/unassign users
|
||||
can :manage_users_in_module do |user, my_module|
|
||||
user.is_owner_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: assign/unassign repository record
|
||||
# NOTE: Use 'module_page? &&' before calling this permission!
|
||||
can :assign_repository_rows_to_module do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: assign/unassign sample
|
||||
# NOTE: Use 'module_page? &&' before calling this permission!
|
||||
can :assign_sample_to_module do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: change_flow_status
|
||||
can :change_my_module_flow_status do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: create comment
|
||||
# result: create comment
|
||||
# step: create comment
|
||||
can :create_comments_in_module do |user, my_module|
|
||||
can_create_comments_in_project?(user, my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: create a snapshot of repository item
|
||||
can :create_my_module_repository_snapshot do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
|
||||
# module: make a repository snapshot selected
|
||||
can :manage_my_module_repository_snapshots do |user, my_module|
|
||||
user.is_technician_or_higher_of_project?(my_module.experiment.project)
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
<div class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
data-url="<%= complete_my_module_my_module_path(@my_module) %>"
|
||||
id="completed-task-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<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 'my_modules.buttons.complete' %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><%=t 'my_modules.complete_modal.description' %></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="modal"><%=t 'my_modules.complete_modal.leave_uncompleted' %></button>
|
||||
<button type="button"
|
||||
class="btn btn-success"
|
||||
data-action="complete"><%=t 'my_modules.buttons.complete' %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||
<% if my_module.completed? %>
|
||||
<strong class="alert-green">
|
||||
<%= t('my_modules.states.completed_on', date: l(my_module.completed_on, format: :full_date)) %>
|
||||
</strong>
|
||||
<% else %>
|
||||
<strong><%= t('my_modules.states.in_progress') %></strong>
|
||||
<% end %>
|
|
@ -20,17 +20,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-block">
|
||||
<div class="flex-block-label">
|
||||
<span class="fas block-icon fa-tachometer-alt"></span>
|
||||
<%= t('my_modules.states.state_label') %>
|
||||
</div>
|
||||
<span class="task-state-label">
|
||||
<%= render partial: "module_state_label.html.erb",
|
||||
locals: { my_module: @my_module } %>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-block">
|
||||
<div class="flex-block-label">
|
||||
<span class="fas block-icon fa-users"></span>
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<button class="btn btn-primary">
|
||||
<i class="fas fa-check"></i>
|
||||
<%= t("my_modules.buttons.complete") %>
|
||||
</button>
|
|
@ -1,4 +0,0 @@
|
|||
<button class="btn btn-secondary ">
|
||||
<i class="fas fa-undo-alt"></i>
|
||||
<%= t("my_modules.buttons.uncomplete") %>
|
||||
</button>
|
|
@ -1,16 +0,0 @@
|
|||
<div class="pull-right my_module-state-buttons">
|
||||
<% if can_complete_module?(@my_module) %>
|
||||
<div class="btn-group">
|
||||
<% if !@my_module.completed? %>
|
||||
<div data-action="complete-task" data-link-url="<%= toggle_task_state_my_module_path(@my_module) %>">
|
||||
<%= render 'my_modules/state_button_complete.html.erb' %>
|
||||
</div>
|
||||
<% else @my_module.completed? %>
|
||||
<div data-action="uncomplete-task" data-link-url="<%= toggle_task_state_my_module_path(@my_module) %>">
|
||||
<%= render 'my_modules/state_button_uncomplete.html.erb' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<span data-hook="my_module-state-buttons"></span>
|
||||
</div>
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<div class="content-pane my-modules-protocols-index" data-task-id="<%= @my_module.id %>">
|
||||
<!-- Details -->
|
||||
<div class="task-section">
|
||||
<div class="task-section task-information">
|
||||
<div class="task-section-header">
|
||||
<span id="taskDetailsLabel" class="task-section-title">
|
||||
<h2>
|
||||
|
@ -20,12 +20,13 @@
|
|||
</div>
|
||||
</span>
|
||||
|
||||
<div class="actions-block">
|
||||
<%= render partial: "my_modules/state_buttons.html.erb" %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="task-details">
|
||||
<%= render partial: "my_module_details" %>
|
||||
<%= render partial: 'my_module_details' %>
|
||||
</div>
|
||||
<div class="task-flows">
|
||||
<%= render partial: 'my_modules/status_flow/task_flow_button', locals: { my_module: @my_module } if @my_module.my_module_status_flow && can_change_my_module_flow_status?(@my_module) %>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Notes -->
|
||||
|
@ -118,9 +119,6 @@
|
|||
<!-- Import protocol elements -->
|
||||
<%= render partial: "protocols/import_export/import_elements.html.erb" %>
|
||||
|
||||
<!-- Complete task modal -->
|
||||
<%= render partial: 'my_modules/complete_task_modal.html.erb' %>
|
||||
|
||||
<!-- Create new office file modal -->
|
||||
<%= render partial: 'assets/wopi/create_wopi_file_modal.html.erb' %>
|
||||
|
||||
|
|
31
app/views/my_modules/status_flow/_task_flow_button.html.erb
Normal file
31
app/views/my_modules/status_flow/_task_flow_button.html.erb
Normal file
|
@ -0,0 +1,31 @@
|
|||
<% status = my_module.my_module_status %>
|
||||
<div class="status-label"><%= t('my_module_statuses.dropdown.status_label') %></div>
|
||||
<div class="dropdown sci-dropdown status-flow-dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
style="<%= "background-color: ##{status.color}" %>;">
|
||||
<span><%= status.name %></span>
|
||||
<span class="caret pull-right"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownTaskFlow" id="dropdownTaskFlowList" data-link-url="<%= update_state_my_module_url(my_module) %>">
|
||||
<% unless status.initial_status? %>
|
||||
<% previous_s = status.previous_status %>
|
||||
<li data-state-id="<%= previous_s.id %>">
|
||||
<span><%= t 'my_module_statuses.dropdown.return_label' %></span> <span style="<%= "background-color: ##{previous_s.color}" %>"><%= previous_s.name %></span>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% unless status.final_status? %>
|
||||
<% next_s = status.next_status %>
|
||||
<li data-state-id="<%= next_s.id %>">
|
||||
<span><%= t('my_module_statuses.dropdown.move_label') %></span> <span style="<%= "background-color: ##{next_s.color}" %>"><%= next_s.name %></span>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<span><a href="#"><%= t('my_module_statuses.dropdown.view_flow_label') %></a></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -650,12 +650,6 @@ en:
|
|||
import: "Import protocol"
|
||||
export: "Export protocol"
|
||||
save_to_repo: "Save to repository"
|
||||
buttons:
|
||||
complete: "Complete task"
|
||||
uncomplete: "Uncomplete task"
|
||||
complete_modal:
|
||||
description: 'You have completed all steps in the task. Would you like to mark entire task as completed?'
|
||||
leave_uncompleted: 'Leave task in progress'
|
||||
description:
|
||||
title: "Edit task %{module} description"
|
||||
label: "Description"
|
||||
|
@ -2191,8 +2185,6 @@ en:
|
|||
assign_user_to_team: "<i>%{assigned_user}</i> was added as %{role} to team <strong>%{team}</strong> by <i>%{assigned_by_user}</i>."
|
||||
unassign_user_from_team: "<i>%{unassigned_user}</i> was removed from team <strong>%{team}</strong> by <i>%{unassigned_by_user}</i>."
|
||||
task_completed: "%{user} completed task %{module}. %{date} | Project: %{project} | Experiment: %{experiment}"
|
||||
start_work_on_next_task: "<i>%{user}</i> has completed the task <strong>%{module}</strong>. You can now start working on the next task in the workflow."
|
||||
start_work_on_next_task_message: "Project: %{project} | Experiment: %{experiment} | Task: %{my_module}"
|
||||
|
||||
assets:
|
||||
head_title:
|
||||
|
|
10
config/locales/my_module_statuses/en.yml
Normal file
10
config/locales/my_module_statuses/en.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
en:
|
||||
my_module_statuses:
|
||||
dropdown:
|
||||
status_label: Status
|
||||
move_label: Move to ->
|
||||
return_label: Return to ->
|
||||
view_flow_label: View task flow
|
||||
update_status:
|
||||
error:
|
||||
no_permission: You dont have permission to change the status
|
|
@ -18,28 +18,6 @@ Rails.application.routes.draw do
|
|||
|
||||
root 'dashboards#show'
|
||||
|
||||
# # Client APP endpoints
|
||||
# get '/settings', to: 'client_api/settings#index'
|
||||
# get '/settings/*all', to: 'client_api/settings#index'
|
||||
#
|
||||
# namespace :client_api, defaults: { format: 'json' } do
|
||||
# post '/premissions', to: 'permissions#status'
|
||||
# %i(activities teams notifications users configurations).each do |path|
|
||||
# draw path
|
||||
# end
|
||||
# end
|
||||
|
||||
# Save sample table state
|
||||
# post '/state_save/:team_id/:user_id',
|
||||
# to: 'user_samples#save_samples_table_status',
|
||||
# as: 'save_samples_table_status',
|
||||
# defaults: { format: 'json' }
|
||||
#
|
||||
# post '/state_load/:team_id/:user_id',
|
||||
# to: 'user_samples#load_samples_table_status',
|
||||
# as: 'load_samples_table_status',
|
||||
# defaults: { format: 'json' }
|
||||
|
||||
resources :activities, only: [:index]
|
||||
|
||||
get 'forbidden', to: 'application#forbidden', as: 'forbidden'
|
||||
|
@ -187,18 +165,7 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
end
|
||||
# resources :samples, only: [:new, :create]
|
||||
# resources :sample_types, except: [:show, :new] do
|
||||
# get 'sample_type_element', to: 'sample_types#sample_type_element'
|
||||
# get 'destroy_confirmation', to: 'sample_types#destroy_confirmation'
|
||||
# end
|
||||
# resources :sample_groups, except: [:show, :new] do
|
||||
# get 'sample_group_element', to: 'sample_groups#sample_group_element'
|
||||
# get 'destroy_confirmation', to: 'sample_groups#destroy_confirmation'
|
||||
# end
|
||||
# resources :custom_fields, only: [:create, :edit, :update, :destroy] do
|
||||
# get 'destroy_html'
|
||||
# end
|
||||
|
||||
member do
|
||||
post 'parse_sheet', defaults: { format: 'json' }
|
||||
post 'export_repository', to: 'repositories#export_repository'
|
||||
|
@ -425,11 +392,10 @@ Rails.application.routes.draw do
|
|||
patch 'protocol_description',
|
||||
to: 'my_modules#update_protocol_description',
|
||||
as: 'update_protocol_description'
|
||||
patch 'state', to: 'my_modules#update_state', as: 'update_state'
|
||||
get 'protocols' # Protocols view for single module
|
||||
get 'results' # Results view for single module
|
||||
get 'archive' # Archive view for single module
|
||||
get 'complete_my_module'
|
||||
post 'toggle_task_state'
|
||||
end
|
||||
|
||||
# Those routes are defined outside of member block
|
||||
|
|
|
@ -105,16 +105,3 @@ Given default screen size2
|
|||
And I fill in "I will go to Krn one day." in "#my_module_description_textarea" rich text editor field
|
||||
And I click element with css ".tinymce-save-button"
|
||||
Then I should see "I will go to Krn one day."
|
||||
|
||||
@javascript
|
||||
Scenario: Successful Complete task
|
||||
Given I'm on the Protocols page of a "Experiment design" task
|
||||
And I click "Complete task" button
|
||||
Then I should see "Uncomplete task"
|
||||
|
||||
@javascript
|
||||
Scenario: Successful Uncomplete task
|
||||
Given I'm on the Protocols page of a "Experiment design" task
|
||||
And I click "Complete task" button
|
||||
And I click "Uncomplete task" button
|
||||
Then I should see "Complete task"
|
||||
|
|
|
@ -123,39 +123,77 @@ describe MyModulesController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'POST toggle_task_state' do
|
||||
let(:action) { post :toggle_task_state, params: params, format: :json }
|
||||
let(:params) { { id: my_module.id } }
|
||||
describe 'PUT update_state' do
|
||||
let(:action) { put :update_state, params: params, format: :json }
|
||||
let(:my_module_id) { my_module.id }
|
||||
let(:status_id) { 'some-state-id' }
|
||||
let(:params) do
|
||||
{
|
||||
id: my_module_id,
|
||||
my_module: { status_id: status_id }
|
||||
}
|
||||
end
|
||||
let(:my_module_status_flow) { create :my_module_status_flow, :in_team, team: team}
|
||||
let(:status1) {create :my_module_status, my_module_status_flow: my_module_status_flow}
|
||||
let(:status2) {create :my_module_status, my_module_status_flow: my_module_status_flow}
|
||||
|
||||
context 'when completing task' do
|
||||
let(:my_module) do
|
||||
create :my_module, state: 'uncompleted', experiment: experiment
|
||||
context 'when states updated' do
|
||||
let(:status_id) { status2.id }
|
||||
|
||||
before do
|
||||
my_module.update(my_module_status: status1)
|
||||
end
|
||||
|
||||
it 'calls create activity for completing task' do
|
||||
expect(Activities::CreateActivityService)
|
||||
.to(receive(:call)
|
||||
.with(hash_including(activity_type: :complete_task)))
|
||||
it 'changes status' do
|
||||
action
|
||||
|
||||
expect(my_module.reload.my_module_status.name).to be_eql(status2.name)
|
||||
expect(response).to have_http_status 200
|
||||
end
|
||||
end
|
||||
|
||||
context 'when uncompleting task' do
|
||||
let(:my_module) do
|
||||
create :my_module, state: 'completed', experiment: experiment
|
||||
context 'when status not found' do
|
||||
let(:status_id) { -1 }
|
||||
|
||||
before do
|
||||
my_module.update(my_module_status: status1)
|
||||
end
|
||||
|
||||
it 'calls create activity for uncompleting task' do
|
||||
expect(Activities::CreateActivityService)
|
||||
.to(receive(:call)
|
||||
.with(hash_including(activity_type: :uncomplete_task)))
|
||||
it 'renders 404' do
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds activity in DB' do
|
||||
expect { action }
|
||||
.to(change { Activity.count })
|
||||
context 'when my_module does not have assign flow yet' do
|
||||
let(:status_id) { -1 }
|
||||
|
||||
it 'renders 404' do
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 404
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have permissions' do
|
||||
it 'renders 403' do
|
||||
# Remove user from project
|
||||
UserProject.where(user: user, project: project).destroy_all
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 403
|
||||
end
|
||||
end
|
||||
|
||||
context 'when my_module not found' do
|
||||
let(:my_module_id) { -1 }
|
||||
|
||||
it 'renders 404' do
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 404
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue