From f94d9cc0f2b38aa36631ac505744ad60e511334c Mon Sep 17 00:00:00 2001 From: Soufiane Date: Tue, 25 Apr 2023 14:00:55 +0200 Subject: [PATCH 01/14] Direct inventory item assignment to the current task from the item card BE [SCI-8142] (#5163) Implement controller action to directly assign item to task [SCI-8142] --- .../my_module_repositories_controller.rb | 34 +++++++++++++++++-- config/locales/en.yml | 2 ++ config/routes.rb | 2 +- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/controllers/my_module_repositories_controller.rb b/app/controllers/my_module_repositories_controller.rb index 5b5bee11f..5a1258762 100644 --- a/app/controllers/my_module_repositories_controller.rb +++ b/app/controllers/my_module_repositories_controller.rb @@ -4,11 +4,11 @@ class MyModuleRepositoriesController < ApplicationController include ApplicationHelper before_action :load_my_module - before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html) + before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html create) before_action :check_my_module_view_permissions, except: %i(update consume_modal update_consumption) - before_action :check_repository_view_permissions, except: %i(repositories_dropdown_list repositories_list_html) + before_action :check_repository_view_permissions, except: %i(repositories_dropdown_list repositories_list_html create) before_action :check_repository_row_consumption_permissions, only: %i(consume_modal update_consumption) - before_action :check_assign_repository_records_permissions, only: :update + before_action :check_assign_repository_records_permissions, only: %i(update create) def index_dt @draw = params[:draw].to_i @@ -41,6 +41,34 @@ class MyModuleRepositoriesController < ApplicationController render rows_view end + def create + repository_row = RepositoryRow.find(params[:repository_row_id]) + repository = repository_row.repository + return render_403 unless can_read_repository?(repository) + + ActiveRecord::Base.transaction do + @my_module.my_module_repository_rows.create!(repository_row: repository_row, assigned_by: current_user) + + Activities::CreateActivityService.call(activity_type: :assign_repository_record, + owner: current_user, + team: @my_module.experiment.project.team, + project: @my_module.experiment.project, + subject: @my_module, + message_items: { my_module: @my_module.id, + repository: repository.id, + record_names: repository_row.name }) + + render json: { + flash: t('my_modules.assigned_items.direct_assign.success') + } + end + rescue StandardError => e + Rails.logger.error e.message + render json: { + flash: t('my_modules.repository.flash.update_error') + }, status: :bad_request + end + def update service = RepositoryRows::MyModuleAssignUnassignService.call(my_module: @my_module, repository: @repository, diff --git a/config/locales/en.yml b/config/locales/en.yml index d10e8bac5..5fda93e9c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1007,6 +1007,8 @@ en: assigned_items: title: "Assigned items" assign_from: "Assign from" + direct_assign: + success: "Successfully assigned an item to the task." protocol: title: "Protocol" options_dropdown: diff --git a/config/routes.rb b/config/routes.rb index d7e9dd634..4253985a9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -432,7 +432,7 @@ Rails.application.routes.draw do get :repositories_dropdown_list, controller: :my_module_repositories get :repositories_list_html, controller: :my_module_repositories - resources :repositories, controller: :my_module_repositories, only: :update do + resources :repositories, controller: :my_module_repositories, only: %i(update create) do member do get :full_view_table post :index_dt From af9219b7ae51f3d0e1bfe2bf79929bdafa3a3b2e Mon Sep 17 00:00:00 2001 From: aignatov-bio <47317017+aignatov-bio@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:30:14 +0200 Subject: [PATCH 02/14] Assign item through repository item modal [SCI-8141] (#5237) --- .../sitewide/repository_row_info_modal.js | 6 ++++++ .../repository/repository_row_modal.scss | 11 ++++++++++ app/controllers/repository_rows_controller.rb | 10 ++++++++++ .../_repository_row_info_modal.html.erb | 20 +++++++++++++++---- config/locales/en.yml | 4 ++++ 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/sitewide/repository_row_info_modal.js b/app/assets/javascripts/sitewide/repository_row_info_modal.js index 3f896c490..df4db4864 100644 --- a/app/assets/javascripts/sitewide/repository_row_info_modal.js +++ b/app/assets/javascripts/sitewide/repository_row_info_modal.js @@ -5,9 +5,14 @@ $(document).on('click', '.record-info-link', function(e) { var that = $(this); + let params = {}; + if ($('.my-modules-protocols-index').length) { + params.my_module_id = $('.my-modules-protocols-index').data('task-id'); + } $.ajax({ method: 'GET', url: that.attr('href'), + data: params, dataType: 'json' }).done(function(xhr, settings, data) { if ($('#modal-info-repository-row').length) { @@ -16,6 +21,7 @@ $('.modal-backdrop').remove(); } $('body').append($.parseHTML(data.responseJSON.html)); + $('[data-toggle="tooltip"]').tooltip(); $('#modal-info-repository-row').modal('show', { backdrop: true, keyboard: false diff --git a/app/assets/stylesheets/repository/repository_row_modal.scss b/app/assets/stylesheets/repository/repository_row_modal.scss index a25126b89..3399f7ec9 100644 --- a/app/assets/stylesheets/repository/repository_row_modal.scss +++ b/app/assets/stylesheets/repository/repository_row_modal.scss @@ -3,4 +3,15 @@ display: flex; justify-content: flex-end; } + + .modal-footer[data-assign-item-button="true"] { + align-items: center; + display: flex; + gap: .5em; + width: 100%; + + .print-label-button { + margin-right: auto; + } + } } diff --git a/app/controllers/repository_rows_controller.rb b/app/controllers/repository_rows_controller.rb index 9376ae2d3..790d8f35e 100644 --- a/app/controllers/repository_rows_controller.rb +++ b/app/controllers/repository_rows_controller.rb @@ -62,9 +62,19 @@ class RepositoryRowsController < ApplicationController def show @repository_row = RepositoryRow.find_by(id: params[:id]) + @my_module = MyModule.find_by(id: params[:my_module_id]) return render_404 unless @repository_row return render_404 unless @repository_row.repository_id == params[:repository_id].to_i return render_403 unless can_read_repository?(@repository_row.repository) + return render_403 if @my_module && !can_read_my_module?(@my_module) + + if @my_module + @my_module_assign_error = if !can_assign_my_module_repository_rows?(@my_module) + I18n.t('repository_row.modal_info.assign_to_task_error.no_access') + elsif @repository_row.my_modules.where(id: @my_module.id).any? + I18n.t('repository_row.modal_info.assign_to_task_error.already_assigned') + end + end @assigned_modules = @repository_row.my_modules.joins(experiment: :project) @viewable_modules = @assigned_modules.viewable_by_user(current_user, current_user.teams) diff --git a/app/views/repositories/_repository_row_info_modal.html.erb b/app/views/repositories/_repository_row_info_modal.html.erb index 003acaca6..096e06041 100644 --- a/app/views/repositories/_repository_row_info_modal.html.erb +++ b/app/views/repositories/_repository_row_info_modal.html.erb @@ -111,10 +111,22 @@ <% end %> <% end %> - diff --git a/config/locales/en.yml b/config/locales/en.yml index 5fda93e9c..1f5e8d61e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2173,6 +2173,10 @@ en: no_tasks: "This item in not assigned to any task." amount: "Amount: %{value}" unit: "Unit: %{unit}" + assign_to_task: "Assign to this task" + assign_to_task_error: + no_access: "You can only view this task" + already_assigned: "This item is already assigned to this task" modal_print_label: head_title: "Print label - %{repository_row}" head_title_multiple: "Print label - %{repository_rows} rows" From d4abcb4980f4e1a0f290a8a90e74496a0d9953e3 Mon Sep 17 00:00:00 2001 From: ajugo Date: Tue, 25 Apr 2023 15:12:19 +0200 Subject: [PATCH 03/14] Implement assigning items to task when adding a smart annotation [SCI-8208] (#5295) --- .../javascripts/my_modules/protocols.js | 3 +- .../javascripts/my_modules/repositories.js | 3 ++ app/assets/javascripts/my_modules/stock.js | 2 +- .../repositories/renderers/new_renderers.js | 2 +- app/assets/javascripts/repositories/stock.js | 2 +- .../javascripts/results/result_texts.js | 8 +++- .../javascripts/shared/inline_editing.js | 2 +- app/assets/javascripts/sitewide/atwho_res.js | 33 +++++++++++--- .../javascripts/sitewide/comments_sidebar.js | 2 +- .../stylesheets/shared/smart_annotation.scss | 43 +++++++++++++++++-- app/controllers/at_who_controller.rb | 4 +- app/javascript/packs/tiny_mce.js | 2 +- app/javascript/vue/protocol/container.vue | 2 + app/javascript/vue/protocol/step.vue | 5 +++ .../vue/protocol/step_elements/checklist.vue | 4 ++ .../vue/protocol/step_elements/table.vue | 4 ++ .../vue/protocol/step_elements/text.vue | 5 +++ app/javascript/vue/shared/inline_edit.vue | 2 +- app/javascript/vue/shared/tinymce.vue | 4 +- app/serializers/protocol_serializer.rb | 8 +++- app/utilities/smart_annotation.rb | 16 ++++++- app/views/result_texts/_edit.html.erb | 1 + app/views/result_texts/_new.html.erb | 1 + .../_atwho_control_buttons.html.erb | 12 ++++++ .../_experiment_items.html.erb | 1 + .../_my_module_items.html.erb | 1 + .../smart_annotation/_project_items.html.erb | 1 + .../_repository_items.html.erb | 1 + config/locales/en.yml | 5 ++- 29 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 app/views/shared/smart_annotation/_atwho_control_buttons.html.erb diff --git a/app/assets/javascripts/my_modules/protocols.js b/app/assets/javascripts/my_modules/protocols.js index c96749baa..183dc905c 100644 --- a/app/assets/javascripts/my_modules/protocols.js +++ b/app/assets/javascripts/my_modules/protocols.js @@ -18,7 +18,8 @@ function initEditMyModuleDescription() { { onSaveCallback: () => { Prism.highlightAllUnder(viewObject.get(0)); - } + }, + assignableMyModuleId: $('#my_module_description_textarea').data('object-id') } ); }).on('click', 'a', function(e) { diff --git a/app/assets/javascripts/my_modules/repositories.js b/app/assets/javascripts/my_modules/repositories.js index 6f0701c28..480865a6c 100644 --- a/app/assets/javascripts/my_modules/repositories.js +++ b/app/assets/javascripts/my_modules/repositories.js @@ -841,6 +841,9 @@ var MyModuleRepositories = (function() { reloadFullViewTable: () => { if (!FULL_VIEW_TABLE) return; FULL_VIEW_TABLE.ajax.reload(null, false); + }, + reloadRepositoriesList: (repositoryId, expand = false) => { + reloadRepositoriesList(repositoryId, expand); } }; }()); diff --git a/app/assets/javascripts/my_modules/stock.js b/app/assets/javascripts/my_modules/stock.js index 688b60bbd..fd40c5408 100644 --- a/app/assets/javascripts/my_modules/stock.js +++ b/app/assets/javascripts/my_modules/stock.js @@ -25,7 +25,7 @@ var MyModuleStockConsumption = (function() { $manageModal.find('.modal-content').html(result.html); $manageModal.modal('show'); focusStockConsumption(); - SmartAnnotation.init($(CONSUMPTION_MODAL + ' #comment')[0]); + SmartAnnotation.init($(CONSUMPTION_MODAL + ' #comment')[0], false); $('#stock_consumption').on('input', function() { let initialValue = new Decimal($(this).data('initial-value') || 0); diff --git a/app/assets/javascripts/repositories/renderers/new_renderers.js b/app/assets/javascripts/repositories/renderers/new_renderers.js index c626f98d0..7310690dd 100644 --- a/app/assets/javascripts/repositories/renderers/new_renderers.js +++ b/app/assets/javascripts/repositories/renderers/new_renderers.js @@ -33,7 +33,7 @@ $.fn.dataTable.render.newRepositoryTextValue = function(formId, columnId, $cell) data-type="RepositoryTextValue"> `); - SmartAnnotation.init($cell.find('input')); + SmartAnnotation.init($cell.find('input'), false); }; $.fn.dataTable.render.newRepositoryListValue = function(formId, columnId, $cell) { diff --git a/app/assets/javascripts/repositories/stock.js b/app/assets/javascripts/repositories/stock.js index 306543708..82a43a644 100644 --- a/app/assets/javascripts/repositories/stock.js +++ b/app/assets/javascripts/repositories/stock.js @@ -120,7 +120,7 @@ var RepositoryStockValues = (function() { this.value = formatDecimalValue(this.value, decimals); }); - SmartAnnotation.init($('#repository-stock-value-comment')[0]); + SmartAnnotation.init($('#repository-stock-value-comment')[0], false); $('#repository-stock-value-comment').on('input', function() { $(this).closest('.sci-input-container').toggleClass( diff --git a/app/assets/javascripts/results/result_texts.js b/app/assets/javascripts/results/result_texts.js index 55df6bb43..b16830a0c 100644 --- a/app/assets/javascripts/results/result_texts.js +++ b/app/assets/javascripts/results/result_texts.js @@ -27,7 +27,9 @@ formAjaxResultText($form); Results.initCancelFormButton($form, initNewReslutText); Results.toggleResultEditButtons(false); - TinyMCE.init('#result_text_attributes_textarea'); + TinyMCE.init('#result_text_attributes_textarea', { + assignableMyModuleId: $('#result_text_attributes_textarea').data('my-module-id') + }); $('#result_name').focus(); }, error: function() { @@ -58,7 +60,9 @@ Results.toggleResultEditButtons(true); }); Results.toggleResultEditButtons(false); - TinyMCE.init('#result_text_attributes_textarea'); + TinyMCE.init('#result_text_attributes_textarea', { + assignableMyModuleId: $('#result_text_attributes_textarea').data('my-module-id') + }); $('#result_name').focus(); }); } diff --git a/app/assets/javascripts/shared/inline_editing.js b/app/assets/javascripts/shared/inline_editing.js index 17a113864..1359b33da 100644 --- a/app/assets/javascripts/shared/inline_editing.js +++ b/app/assets/javascripts/shared/inline_editing.js @@ -18,7 +18,7 @@ var inlineEditing = (function() { function initSmartAnnotation(container) { if (container.data('smart-annotation')) { - SmartAnnotation.init(inputField(container)); + SmartAnnotation.init(inputField(container), false); } } diff --git a/app/assets/javascripts/sitewide/atwho_res.js b/app/assets/javascripts/sitewide/atwho_res.js index b9ce45dc0..486fbe800 100644 --- a/app/assets/javascripts/sitewide/atwho_res.js +++ b/app/assets/javascripts/sitewide/atwho_res.js @@ -1,4 +1,4 @@ -/* global _ */ +/* global PerfectScrollbar MyModuleRepositories HelperModule _ */ var SmartAnnotation = (function() { 'use strict'; @@ -11,7 +11,7 @@ var SmartAnnotation = (function() { }); } - function SetAtWho(field, deferred) { + function SetAtWho(field, deferred, assignableMyModuleId) { var FilterTypeEnum = Object.freeze({ USER: { tag: 'users', dataUrl: $(document.body).attr('data-atwho-users-url') }, TASK: { tag: 'sa-tasks', dataUrl: $(document.body).attr('data-atwho-task-url') }, @@ -67,6 +67,7 @@ var SmartAnnotation = (function() { let activeRepository = repositoryTab.find('.btn-primary'); if (activeRepository.length) { params.repository_id = activeRepository.data('object-id'); + params.assignable_my_module_id = assignableMyModuleId; } } $.getJSON(filterType.dataUrl, params, function(data) { @@ -81,6 +82,10 @@ var SmartAnnotation = (function() { $currentAtWho.find(`.repository-object[data-object-id="${data.repository}"]`) .addClass('btn-primary').removeClass('btn-light'); } + if ($('.atwho-scroll-container')[0]) { + // eslint-disable-next-line no-new + new PerfectScrollbar($('.atwho-scroll-container')[0]); + } }); return true; }, @@ -141,6 +146,24 @@ var SmartAnnotation = (function() { $(this).addClass('btn-primary').removeClass('btn-light'); $(field).click().focus(); }); + $currentAtWho.on('click', '.atwho-assign-button', function() { + let el = $(this); + $.ajax({ + method: 'POST', + url: el.data('assign-url'), + data: { repository_row_id: el.data('repository-row-id') }, + dataType: 'json', + success: function(data) { + if (typeof MyModuleRepositories !== 'undefined') { + MyModuleRepositories.reloadRepositoriesList(el.data('repository-id')); + } + HelperModule.flashAlertMsg(data.flash, 'success'); + }, + error: function(response) { + HelperModule.flashAlertMsg(response.responseJSON.flash, 'danger'); + } + }); + }); if ($currentAtWho.find('.tab-pane.active').length === 0) { let filterType = DEFAULT_SEARCH_FILTER.tag; @@ -223,8 +246,8 @@ var SmartAnnotation = (function() { $('.atwho-header-res').find('.fa-times').click(); } - function initialize(field, deferred) { - var atWho = new SetAtWho(field, deferred); + function initialize(field, deferred, assignableMyModuleId) { + var atWho = new SetAtWho(field, deferred, assignableMyModuleId); atWho.init(); } @@ -240,7 +263,7 @@ var SmartAnnotation = (function() { (function() { $(document).on('focus', '[data-atwho-edit]', function() { if (_.isUndefined($(this).data('atwho'))) { - SmartAnnotation.init(this); + SmartAnnotation.init(this, false); } }); diff --git a/app/assets/javascripts/sitewide/comments_sidebar.js b/app/assets/javascripts/sitewide/comments_sidebar.js index f59f5075f..d6a35193a 100644 --- a/app/assets/javascripts/sitewide/comments_sidebar.js +++ b/app/assets/javascripts/sitewide/comments_sidebar.js @@ -147,7 +147,7 @@ var CommentsSidebar = (function() { function initInputField() { if ($(SIDEBAR).find('.comment-input-field').length) { - SmartAnnotation.init($(SIDEBAR).find('.comment-input-field')); + SmartAnnotation.init($(SIDEBAR).find('.comment-input-field'), false); } } diff --git a/app/assets/stylesheets/shared/smart_annotation.scss b/app/assets/stylesheets/shared/smart_annotation.scss index b9500cc51..fb0563dc7 100644 --- a/app/assets/stylesheets/shared/smart_annotation.scss +++ b/app/assets/stylesheets/shared/smart_annotation.scss @@ -1,3 +1,6 @@ +// scss-lint:disable SelectorDepth +// scss-lint:disable NestingDepth + .atwho-view { background: $color_white; border-radius: $border-radius-default; @@ -107,15 +110,49 @@ .item { cursor: pointer; - margin-left: -.5em; + line-height: 2.25em; overflow: hidden; - padding: .25em .5em; + padding: 0 .5em; + position: relative; text-overflow: ellipsis; - width: calc(100% + 1em); + vertical-align: middle; white-space: nowrap; + width: 100%; + + .atwho-button-container { + background: linear-gradient(90deg, + transparent, + $color-concrete 15%, + $color-concrete 100%); + display: inline; + opacity: 0; + padding-left: 2em; + position: absolute; + right: 0; + + .atwho-assign-button-form { + display: inline; + } + + .atwho-insert-button, + .atwho-assign-button { + background: $color-concrete; + color: $brand-primary; + height: 2.25em; + margin-right: .5em; + padding: 0 .5em; + text-align: center; + width: auto; + } + } &.cur { background: $color-concrete; + color: $brand-primary; + + .atwho-button-container { + opacity: 1; + } } .atwho-highlight { diff --git a/app/controllers/at_who_controller.rb b/app/controllers/at_who_controller.rb index 6c01dc05a..892889aa2 100644 --- a/app/controllers/at_who_controller.rb +++ b/app/controllers/at_who_controller.rb @@ -40,7 +40,7 @@ class AtWhoController < ApplicationController end if repository && can_read_repository?(repository) items = SmartAnnotation.new(current_user, current_team, @query) - .repository_rows(repository) + .repository_rows(repository, params[:assignable_my_module_id]) repository_id = repository.id else items = [] @@ -51,7 +51,7 @@ class AtWhoController < ApplicationController render json: { res: [ render_to_string(partial: 'shared/smart_annotation/repository_items.html.erb', - locals: { repository_rows: items }) + locals: { repository_rows: items, repository: repository }) ], repository: repository_id, team: current_team.id diff --git a/app/javascript/packs/tiny_mce.js b/app/javascript/packs/tiny_mce.js index c53b532dc..254153622 100644 --- a/app/javascript/packs/tiny_mce.js +++ b/app/javascript/packs/tiny_mce.js @@ -368,7 +368,7 @@ window.TinyMCE = (() => { editor.selection.select(editor.getBody(), true); editor.selection.collapse(false); - SmartAnnotation.init($(editor.contentDocument.activeElement)); + SmartAnnotation.init($(editor.contentDocument.activeElement), false, options.assignableMyModuleId); SmartAnnotation.preventPropagation('.atwho-user-popover'); if (options.afterInitCallback) { options.afterInitCallback(); } diff --git a/app/javascript/vue/protocol/container.vue b/app/javascript/vue/protocol/container.vue index 1259a32b9..41a97e1db 100644 --- a/app/javascript/vue/protocol/container.vue +++ b/app/javascript/vue/protocol/container.vue @@ -76,6 +76,7 @@ :objectId="parseInt(protocol.id)" :fieldName="'protocol[description]'" :lastUpdated="protocol.attributes.updated_at" + :assignableMyModuleId="protocol.attributes.assignable_my_module_id" :characterLimit="100000" @update="updateDescription" /> @@ -136,6 +137,7 @@ @stepUpdated="refreshProtocolStatus" @step:insert="updateStepsPosition" :reorderStepUrl="steps.length > 1 ? urls.reorder_steps_url : null" + :assignableMyModuleId="protocol.attributes.assignable_my_module_id" /> diff --git a/app/javascript/vue/protocol/step.vue b/app/javascript/vue/protocol/step.vue index b0bd5f9d6..ad18ff6f8 100644 --- a/app/javascript/vue/protocol/step.vue +++ b/app/javascript/vue/protocol/step.vue @@ -136,6 +136,7 @@ :element.sync="elements[index]" :inRepository="inRepository" :reorderElementUrl="elements.length > 1 ? urls.reorder_elements_url : ''" + :assignableMyModuleId="assignableMyModuleId" :isNew="element.isNew" @component:delete="deleteElement" @update="updateElement" @@ -213,6 +214,10 @@ }, reorderStepUrl: { required: false + }, + assignableMyModuleId: { + type: Number, + required: false } }, data() { diff --git a/app/javascript/vue/protocol/step_elements/checklist.vue b/app/javascript/vue/protocol/step_elements/checklist.vue index 0563aab03..3ec36878f 100644 --- a/app/javascript/vue/protocol/step_elements/checklist.vue +++ b/app/javascript/vue/protocol/step_elements/checklist.vue @@ -104,6 +104,10 @@ isNew: { type: Boolean, default: false + }, + assignableMyModuleId: { + type: Number, + required: false } }, data() { diff --git a/app/javascript/vue/protocol/step_elements/table.vue b/app/javascript/vue/protocol/step_elements/table.vue index 86a7e61da..9ed97f684 100644 --- a/app/javascript/vue/protocol/step_elements/table.vue +++ b/app/javascript/vue/protocol/step_elements/table.vue @@ -83,6 +83,10 @@ }, isNew: { type: Boolean, default: false + }, + assignableMyModuleId: { + type: Number, + required: false } }, data() { diff --git a/app/javascript/vue/protocol/step_elements/text.vue b/app/javascript/vue/protocol/step_elements/text.vue index 6d2357f50..2c148fb20 100644 --- a/app/javascript/vue/protocol/step_elements/text.vue +++ b/app/javascript/vue/protocol/step_elements/text.vue @@ -26,6 +26,7 @@ :objectId="element.attributes.orderable.id" :fieldName="'step_text[text]'" :lastUpdated="element.attributes.orderable.updated_at" + :assignableMyModuleId="assignableMyModuleId" :characterLimit="100000" @update="update" @editingDisabled="disableEditMode" @@ -64,6 +65,10 @@ isNew: { type: Boolean, default: false + }, + assignableMyModuleId: { + type: Number, + required: false } }, data() { diff --git a/app/javascript/vue/shared/inline_edit.vue b/app/javascript/vue/shared/inline_edit.vue index aa6b6d25a..84c077e92 100644 --- a/app/javascript/vue/shared/inline_edit.vue +++ b/app/javascript/vue/shared/inline_edit.vue @@ -144,7 +144,7 @@ this.$refs.input.select(); } if (this.smartAnnotation) { - SmartAnnotation.init($(this.$refs.input)); + SmartAnnotation.init($(this.$refs.input), false); } }) this.$emit('editingEnabled'); diff --git a/app/javascript/vue/shared/tinymce.vue b/app/javascript/vue/shared/tinymce.vue index 8640fc092..7f29a9e01 100644 --- a/app/javascript/vue/shared/tinymce.vue +++ b/app/javascript/vue/shared/tinymce.vue @@ -68,6 +68,7 @@ fieldName: String, lastUpdated: Number, inEditMode: Boolean, + assignableMyModuleId: Number, characterLimit: { type: Number, default: null @@ -147,7 +148,8 @@ this.initCharacterCount(); this.$emit('editingEnabled'); }, - placeholder: this.placeholder + placeholder: this.placeholder, + assignableMyModuleId: this.assignableMyModuleId } ) }, diff --git a/app/serializers/protocol_serializer.rb b/app/serializers/protocol_serializer.rb index c4f058c0e..5d9cbecd0 100644 --- a/app/serializers/protocol_serializer.rb +++ b/app/serializers/protocol_serializer.rb @@ -9,7 +9,7 @@ class ProtocolSerializer < ActiveModel::Serializer attributes :name, :id, :urls, :description, :description_view, :updated_at, :in_repository, :created_at_formatted, :updated_at_formatted, :added_by, :authors, :keywords, :version, :code, - :published, :version_comment, :archived, :linked, :has_draft + :published, :version_comment, :archived, :linked, :has_draft, :assignable_my_module_id def updated_at object.updated_at.to_i @@ -97,6 +97,12 @@ class ProtocolSerializer < ActiveModel::Serializer object.linked? end + def assignable_my_module_id + return if in_repository + + object.my_module&.id + end + private def load_from_repo_url diff --git a/app/utilities/smart_annotation.rb b/app/utilities/smart_annotation.rb index f12d8087d..b428de251 100644 --- a/app/utilities/smart_annotation.rb +++ b/app/utilities/smart_annotation.rb @@ -35,13 +35,21 @@ class SmartAnnotation .limit(Constants::ATWHO_SEARCH_LIMIT + 1) end - - def repository_rows(repository) + def repository_rows(repository, my_module_id) res = RepositoryRow .active .where(repository: repository) .search_by_name(@current_user, @current_team, @query, intersect: true) .limit(Constants::ATWHO_SEARCH_LIMIT + 1) + + if my_module_id.present? + res = res.joins('LEFT OUTER JOIN "my_module_repository_rows" "current_my_module_repository_rows"'\ + 'ON "current_my_module_repository_rows"."repository_row_id" = "repository_rows"."id" '\ + 'AND "current_my_module_repository_rows"."my_module_id" = ' + my_module_id.to_s) + .select('repository_rows.id', 'repository_rows.name', + 'CASE WHEN current_my_module_repository_rows.id IS NOT NULL '\ + 'THEN true ELSE false END as row_assigned') + end rep_items_list = [] splitted_name = repository.name.gsub(/[^0-9a-z ]/i, '').split repository_tag = @@ -63,6 +71,10 @@ class SmartAnnotation rep_item[:id] = rep_row.id.base62_encode rep_item[:name] = sanitize(rep_row.name) rep_item[:repository_tag] = repository_tag + if my_module_id.present? + rep_item[:row_assigned] = rep_row&.row_assigned + rep_item[:my_module_id] = my_module_id + end rep_items_list << rep_item end rep_items_list diff --git a/app/views/result_texts/_edit.html.erb b/app/views/result_texts/_edit.html.erb index 8d2fd5f5a..a2c0712d3 100644 --- a/app/views/result_texts/_edit.html.erb +++ b/app/views/result_texts/_edit.html.erb @@ -10,6 +10,7 @@ autocomplete: 'off', data: { object_type: 'result_text', object_id: @result.result_text.id, + my_module_id: @result.my_module_id, last_updated: @result.updated_at.to_i * 1000 }) %> <% end %>
diff --git a/app/views/result_texts/_new.html.erb b/app/views/result_texts/_new.html.erb index 497c616f7..0530069c1 100644 --- a/app/views/result_texts/_new.html.erb +++ b/app/views/result_texts/_new.html.erb @@ -9,6 +9,7 @@ autocomplete: 'off', data: { object_type: 'result_text', object_id: @result.result_text.id, + my_module_id: @result.my_module_id, last_updated: @result.updated_at.to_i * 1000 }) %> <% end %>
diff --git a/app/views/shared/smart_annotation/_atwho_control_buttons.html.erb b/app/views/shared/smart_annotation/_atwho_control_buttons.html.erb new file mode 100644 index 000000000..d3e6bbbc4 --- /dev/null +++ b/app/views/shared/smart_annotation/_atwho_control_buttons.html.erb @@ -0,0 +1,12 @@ +
+ + <% if defined?(row) && !row[:row_assigned] && row[:my_module_id].present? %> + + <%end%> +
diff --git a/app/views/shared/smart_annotation/_experiment_items.html.erb b/app/views/shared/smart_annotation/_experiment_items.html.erb index c91c8c64a..2cb3526e0 100644 --- a/app/views/shared/smart_annotation/_experiment_items.html.erb +++ b/app/views/shared/smart_annotation/_experiment_items.html.erb @@ -10,6 +10,7 @@
  • Exp <%= experiment.name %> + <%= render partial: 'shared/smart_annotation/atwho_control_buttons.html.erb' %>
  • <% end %>
    diff --git a/app/views/shared/smart_annotation/_my_module_items.html.erb b/app/views/shared/smart_annotation/_my_module_items.html.erb index db49bacd0..0fc6b72ce 100644 --- a/app/views/shared/smart_annotation/_my_module_items.html.erb +++ b/app/views/shared/smart_annotation/_my_module_items.html.erb @@ -12,6 +12,7 @@
  • Tsk <%= task.name %> + <%= render partial: 'shared/smart_annotation/atwho_control_buttons.html.erb' %>
  • <% end %>
    diff --git a/app/views/shared/smart_annotation/_project_items.html.erb b/app/views/shared/smart_annotation/_project_items.html.erb index ff6ea9f39..919d6d850 100644 --- a/app/views/shared/smart_annotation/_project_items.html.erb +++ b/app/views/shared/smart_annotation/_project_items.html.erb @@ -4,6 +4,7 @@
  • Prj <%= project.name %> + <%= render partial: 'shared/smart_annotation/atwho_control_buttons.html.erb' %>
  • <% end %> <% if limit_reached %> diff --git a/app/views/shared/smart_annotation/_repository_items.html.erb b/app/views/shared/smart_annotation/_repository_items.html.erb index ae40e198f..dcc2924eb 100644 --- a/app/views/shared/smart_annotation/_repository_items.html.erb +++ b/app/views/shared/smart_annotation/_repository_items.html.erb @@ -4,6 +4,7 @@
  • <%= row[:repository_tag] %> <%= row[:name] %> + <%= render partial: 'shared/smart_annotation/atwho_control_buttons.html.erb', locals: { row: row, repository: repository } %>
  • <% end %> <% if limit_reached %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 1f5e8d61e..b16e45445 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3165,6 +3165,9 @@ en: repository_rows: "Items with this name were not found" users: "Users with this name were not found" description: "Please make sure there are no typos, or erase letters one by one unless you see some results" + buttons: + insert: "Insert" + assign: "Assign to this task" projects: PROJECTS experiments: EXPERIMENTS tasks: TASKS @@ -3173,7 +3176,7 @@ en: users: title: "People" header: "Type the name or email of the user you want to mention (they will be notified)" - help: "Navigate: ↑ ↓ • Submit: Enter / Tab • Dismiss: Esc" + help: "Navigate: ↑ ↓ • Insert: Enter / Tab • Dismiss: Esc" popover_html: "Team: %{team}
    Role: %{role}
    Joined: %{time}" res: archived: "(archived)" From 443cd210901abd49dd10ed28b0b4ba2c26a55cff Mon Sep 17 00:00:00 2001 From: sboursen-scinote Date: Wed, 3 May 2023 13:39:10 +0200 Subject: [PATCH 04/14] WIP: Create a modal for inventory item assignment from inventory page [SCI-8250] --- .../packs/vue/assign_items_to_task_modal.js | 37 ++++ .../assign_items_to_tasks_modal/container.vue | 198 ++++++++++++++++++ app/javascript/vue/shared/select.vue | 1 + app/javascript/vue/shared/select_search.vue | 8 +- .../_assign_items_to_task_modal.html.erb | 15 ++ app/views/repositories/show.html.erb | 1 + config/locales/en.yml | 16 ++ config/webpack/webpack.config.js | 1 + 8 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 app/javascript/packs/vue/assign_items_to_task_modal.js create mode 100644 app/javascript/vue/assign_items_to_tasks_modal/container.vue create mode 100644 app/views/repositories/_assign_items_to_task_modal.html.erb diff --git a/app/javascript/packs/vue/assign_items_to_task_modal.js b/app/javascript/packs/vue/assign_items_to_task_modal.js new file mode 100644 index 000000000..1c0775e76 --- /dev/null +++ b/app/javascript/packs/vue/assign_items_to_task_modal.js @@ -0,0 +1,37 @@ +import TurbolinksAdapter from 'vue-turbolinks'; +import Vue from 'vue/dist/vue.esm'; +import AssignItemsToTaskModalContainer from '../../vue/assign_items_to_tasks_modal/container.vue'; + +Vue.use(TurbolinksAdapter); +Vue.prototype.i18n = window.I18n; + +function initAssignItemsToTaskModalComponent() { + const container = $('.assign-items-to-task-modal-container'); + if (container.length) { + window.AssignItemsToTaskModalComponent = new Vue({ + el: '.assign-items-to-task-modal-container', + name: 'AssignItemsToTaskModalComponent', + components: { + 'assign-items-to-task-modal-container': AssignItemsToTaskModalContainer + }, + data() { + return { + visibility: false, + urls: { + assign: container.data('assign-url'), + projects: container.data('projects-url'), + experiments: container.data('experiments-url'), + tasks: container.data('tasks-url'), + } + }; + }, + methods: { + closeModal() { + this.visibility = false; + } + } + }); + } +} + +initAssignItemsToTaskModalComponent(); diff --git a/app/javascript/vue/assign_items_to_tasks_modal/container.vue b/app/javascript/vue/assign_items_to_tasks_modal/container.vue new file mode 100644 index 000000000..bffb2b02f --- /dev/null +++ b/app/javascript/vue/assign_items_to_tasks_modal/container.vue @@ -0,0 +1,198 @@ + + + diff --git a/app/javascript/vue/shared/select.vue b/app/javascript/vue/shared/select.vue index 9cb048dfa..2438a8b8f 100644 --- a/app/javascript/vue/shared/select.vue +++ b/app/javascript/vue/shared/select.vue @@ -67,6 +67,7 @@ }, setValue(value) { this.value = value; + this.toggle this.$emit('change', this.value); }, updateOptionPosition() { diff --git a/app/javascript/vue/shared/select_search.vue b/app/javascript/vue/shared/select_search.vue index 44827b109..b20e73179 100644 --- a/app/javascript/vue/shared/select_search.vue +++ b/app/javascript/vue/shared/select_search.vue @@ -1,5 +1,5 @@