diff --git a/.gitignore b/.gitignore
index a16f03f0c..4e459907b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -95,3 +95,7 @@ public/marvin4js-license.cxl
/app/assets/builds/*
!/app/assets/builds/.keep
+
+# Ignore automatically generated js-routes files.
+/app/javascript/routes.js
+/app/javascript/routes.d.ts
diff --git a/Gemfile b/Gemfile
index 31fc95950..19a25a069 100644
--- a/Gemfile
+++ b/Gemfile
@@ -94,6 +94,7 @@ gem 'graphviz'
gem 'cssbundling-rails'
gem 'jsbundling-rails'
+gem 'js-routes'
gem 'tailwindcss-rails', '~> 2.4'
@@ -107,6 +108,7 @@ group :development, :test do
gem 'awesome_print'
gem 'better_errors'
gem 'binding_of_caller'
+ gem 'brakeman', require: false
gem 'bullet'
gem 'byebug'
gem 'factory_bot_rails'
diff --git a/Gemfile.lock b/Gemfile.lock
index deeb09c01..a41d9bd63 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -208,6 +208,8 @@ GEM
debug_inspector (>= 0.0.1)
bootsnap (1.16.0)
msgpack (~> 1.2)
+ brakeman (6.1.2)
+ racc
builder (3.2.4)
bullet (7.0.7)
activesupport (>= 3.0.0)
@@ -386,6 +388,8 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
+ js-routes (2.2.8)
+ railties (>= 4)
jsbundling-rails (1.1.1)
railties (>= 6.0.0)
json (2.6.3)
@@ -797,6 +801,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bootsnap
+ brakeman
bullet
byebug
canaid!
@@ -826,6 +831,7 @@ DEPENDENCIES
image_processing
img2zpl!
jbuilder
+ js-routes
jsbundling-rails
json-jwt
json_matchers
diff --git a/Rakefile b/Rakefile
index 9f98587c3..c172d03df 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,3 +5,5 @@ require File.expand_path('../config/application', __FILE__)
Rails.application.load_tasks
Doorkeeper::Rake.load_tasks
+# Update js-routes file before javascript build
+task 'javascript:build' => 'js:routes:typescript'
diff --git a/VERSION b/VERSION
index 2dba38f44..39fc130ef 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.35.0.2
+1.36.0
diff --git a/app/assets/javascripts/repository_columns/index.js b/app/assets/javascripts/repository_columns/index.js
index d17ad503c..a2e39aa83 100644
--- a/app/assets/javascripts/repository_columns/index.js
+++ b/app/assets/javascripts/repository_columns/index.js
@@ -185,7 +185,7 @@ var RepositoryColumns = (function() {
disableSearch: true,
labelHTML: true,
optionLabel: function(option) {
- return `
+ return `
${option.label}
${option.params.text_description || ''}
`
@@ -284,6 +284,7 @@ var RepositoryColumns = (function() {
let editableRow = ($(el).attr('data-editable-row') === 'true') ? 'has-permissions' : '';
let editUrl = $(el).attr('data-edit-column-url');
let destroyUrl = $(el).attr('data-destroy-column-url');
+ const isDisabled = $(el).attr('data-disabled') === 'true';
let thederName;
if ($(el).find('.modal-tooltiptext').length > 0) {
@@ -315,7 +316,9 @@ var RepositoryColumns = (function() {
-
${thederName}
+
+ ${thederName} ${isDisabled ? `` : ''}
+
${
getColumnTypeText(el, colId) || ``
}
diff --git a/app/assets/javascripts/sitewide/marvinjs_editor.js b/app/assets/javascripts/sitewide/marvinjs_editor.js
index a334bf9f5..2afdc0196 100644
--- a/app/assets/javascripts/sitewide/marvinjs_editor.js
+++ b/app/assets/javascripts/sitewide/marvinjs_editor.js
@@ -202,10 +202,14 @@ var MarvinJsEditorApi = (function() {
}
$(marvinJsModal).modal('hide');
- config.editor.focus();
+ if (config.editor) config.editor.focus();
+
config.button.dataset.inProgress = false;
- if (MarvinJsEditor.saveCallback) MarvinJsEditor.saveCallback();
+ if (MarvinJsEditor.saveCallback) {
+ MarvinJsEditor.saveCallback();
+ delete MarvinJsEditor.saveCallback;
+ }
},
error: function(response) {
if (response.status === 403) {
@@ -264,8 +268,8 @@ var MarvinJsEditorApi = (function() {
MarvinJsEditor.save(config);
} else if (config.mode === 'edit') {
config.objectType = 'Asset';
+ MarvinJsEditor.saveCallback = (() => window.location.reload());
MarvinJsEditor.update(config);
- location.reload();
} else if (config.mode === 'new-tinymce') {
config.objectType = 'TinyMceAsset';
MarvinJsEditor.save(config);
diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css
index 400c99bdf..9e83df130 100644
--- a/app/assets/stylesheets/application.tailwind.css
+++ b/app/assets/stylesheets/application.tailwind.css
@@ -2,6 +2,7 @@
@import "tailwind/buttons";
@import "tailwind/modals";
@import "tailwind/flyouts";
+@import "tailwind/radio";
@import "tailwind/loader.css";
@tailwind base;
@@ -69,6 +70,6 @@ html {
@keyframes shine-lines {
0% { background-position: -150px }
-
+
40%, 100% { background-position: 320px }
}
diff --git a/app/assets/stylesheets/shared/comments_sidebar.scss b/app/assets/stylesheets/shared/comments_sidebar.scss
index a60b508fa..65c081105 100644
--- a/app/assets/stylesheets/shared/comments_sidebar.scss
+++ b/app/assets/stylesheets/shared/comments_sidebar.scss
@@ -110,13 +110,13 @@
}
.send-comment {
+ bottom: 5px;
color: $brand-primary;
cursor: pointer;
display: inline-block;
position: absolute;
right: 5px;
text-align: center;
- top: 5px;
}
}
diff --git a/app/assets/stylesheets/shared_styles/elements/radio_buttons.scss b/app/assets/stylesheets/shared_styles/elements/radio_buttons.scss
index b940efe9e..5b7fd31ca 100644
--- a/app/assets/stylesheets/shared_styles/elements/radio_buttons.scss
+++ b/app/assets/stylesheets/shared_styles/elements/radio_buttons.scss
@@ -1,5 +1,5 @@
// scss-lint:disable SelectorDepth QualifyingElement
-
+/*
:root {
--sci-radio-size: 16px;
}
@@ -85,3 +85,4 @@ input[type="radio"].sci-radio {
}
}
}
+*/
diff --git a/app/assets/stylesheets/tailwind/buttons.css b/app/assets/stylesheets/tailwind/buttons.css
index 9f7001362..121cd8b03 100644
--- a/app/assets/stylesheets/tailwind/buttons.css
+++ b/app/assets/stylesheets/tailwind/buttons.css
@@ -114,7 +114,7 @@
}
.btn.btn-light {
- @apply bg-transparent text-sn-blue border-transparent;
+ @apply bg-transparent text-sn-blue border-transparent bg-sn-white;
}
.btn.btn-light.btn-black {
diff --git a/app/assets/stylesheets/tailwind/radio.css b/app/assets/stylesheets/tailwind/radio.css
new file mode 100644
index 000000000..197119397
--- /dev/null
+++ b/app/assets/stylesheets/tailwind/radio.css
@@ -0,0 +1,42 @@
+@layer components {
+
+ .sci-radio-container {
+ @apply inline-block h-4 w-4 relative;
+ }
+
+ input[type="radio"].sci-radio {
+ @apply cursor-pointer shrink-0 h-4 w-4 m-0 opacity-0 relative z-[2];
+ }
+
+ input[type="radio"].sci-radio + .sci-radio-label {
+ @apply inline-block shrink-0 h-4 w-4 absolute left-0;
+ }
+
+ input[type="radio"].sci-radio + .sci-radio-label::before {
+ @apply border-[1px] border-solid border-sn-black rounded-full text-white text-center transition-all
+ h-4 w-4 left-0 absolute;
+ content: "";
+ }
+
+ input[type="radio"].sci-radio + .sci-radio-label::after{
+ @apply bg-white rounded-full text-white text-center transition-all
+ absolute w-2.5 h-2.5 top-[3px] left-[3px] ;
+ content: "";
+ }
+
+ input[type="radio"].sci-radio:checked + .sci-radio-label::before {
+ @apply !border-sn-blue;
+ }
+
+ input[type="radio"].sci-radio:checked + .sci-radio-label::after {
+ @apply !bg-sn-science-blue;
+ }
+
+ input[type="radio"].sci-radio:disabled + .sci-radio-label::before {
+ @apply !border-sn-sleepy-grey;
+ }
+
+ input[type="radio"].sci-radio:checked:disabled + .sci-radio-label::after {
+ @apply !bg-sn-sleepy-grey;
+ }
+}
diff --git a/app/controllers/access_permissions/projects_controller.rb b/app/controllers/access_permissions/projects_controller.rb
index a50091fa7..3527cfefe 100644
--- a/app/controllers/access_permissions/projects_controller.rb
+++ b/app/controllers/access_permissions/projects_controller.rb
@@ -103,8 +103,6 @@ module AccessPermissions
destroy: true
)
- user_assignment.destroy!
-
log_activity(:unassign_user_from_project, { user_target: user_assignment.user.id,
role: user_assignment.user_role.name })
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 29fa820c3..25aff0c0c 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -10,14 +10,14 @@ class RepositoriesController < ApplicationController
include MyModulesHelper
before_action :load_repository, except: %i(index create create_modal sidebar archive restore actions_toolbar
- export_modal export_repositories)
- before_action :load_repositories, only: :index
+ export_modal export_repositories list)
+ before_action :load_repositories, only: %i(index list)
before_action :load_repositories_for_archiving, only: :archive
before_action :load_repositories_for_restoring, only: :restore
- before_action :check_view_all_permissions, only: %i(index sidebar)
+ before_action :check_view_all_permissions, only: %i(index sidebar list)
before_action :check_view_permissions, except: %i(index create_modal create update destroy parse_sheet
import_records sidebar archive restore actions_toolbar
- export_modal export_repositories)
+ export_modal export_repositories list)
before_action :check_manage_permissions, only: %i(rename_modal update)
before_action :check_delete_permissions, only: %i(destroy destroy_modal)
before_action :check_archive_permissions, only: %i(archive restore)
@@ -44,6 +44,16 @@ class RepositoriesController < ApplicationController
end
end
+ def list
+ results = @repositories
+ results = results.name_like(params[:query]) if params[:query].present?
+ render json: { data: results.map { |r| [r.id, r.name] } }
+ end
+
+ def rows_list
+ render json: { data: @repository.repository_rows.map { |r| [r.id, r.name] } }
+ end
+
def sidebar
render json: {
html: render_to_string(partial: 'repositories/sidebar', locals: {
diff --git a/app/controllers/storage_location_repository_rows_controller.rb b/app/controllers/storage_location_repository_rows_controller.rb
index 416a96536..ae9ed52fb 100644
--- a/app/controllers/storage_location_repository_rows_controller.rb
+++ b/app/controllers/storage_location_repository_rows_controller.rb
@@ -1,19 +1,20 @@
# frozen_string_literal: true
class StorageLocationRepositoryRowsController < ApplicationController
- before_action :load_storage_location_repository_row, only: %i(update destroy)
+ before_action :check_storage_locations_enabled, except: :destroy
+ before_action :load_storage_location_repository_row, only: %i(update destroy move)
before_action :load_storage_location
- before_action :load_repository_row
- before_action :check_read_permissions, only: :index
- before_action :check_manage_permissions, except: :index
+ before_action :load_repository_row, only: %i(create update destroy move)
+ before_action :check_read_permissions, except: %i(create actions_toolbar)
+ before_action :check_manage_permissions, only: %i(create update destroy)
def index
storage_location_repository_row = Lists::StorageLocationRepositoryRowsService.new(
- current_team, storage_location_repository_row_params
+ current_team, params
).call
render json: storage_location_repository_row,
each_serializer: Lists::StorageLocationRepositoryRowSerializer,
- include: %i(repository_row)
+ meta: (pagination_dict(storage_location_repository_row) unless @storage_location.with_grid?)
end
def update
@@ -21,8 +22,7 @@ class StorageLocationRepositoryRowsController < ApplicationController
if @storage_location_repository_row.save
render json: @storage_location_repository_row,
- serializer: Lists::StorageLocationRepositoryRowSerializer,
- include: :repository_row
+ serializer: Lists::StorageLocationRepositoryRowSerializer
else
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
end
@@ -38,13 +38,30 @@ class StorageLocationRepositoryRowsController < ApplicationController
if @storage_location_repository_row.save
render json: @storage_location_repository_row,
- serializer: Lists::StorageLocationRepositoryRowSerializer,
- include: :repository_row
+ serializer: Lists::StorageLocationRepositoryRowSerializer
else
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
end
end
+ def move
+ ActiveRecord::Base.transaction do
+ @storage_location_repository_row.discard
+ @storage_location_repository_row = StorageLocationRepositoryRow.create!(
+ repository_row: @repository_row,
+ storage_location: @storage_location,
+ metadata: storage_location_repository_row_params[:metadata] || {},
+ created_by: current_user
+ )
+
+ render json: @storage_location_repository_row,
+ serializer: Lists::StorageLocationRepositoryRowSerializer
+ rescue ActiveRecord::RecordInvalid => e
+ render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
+ raise ActiveRecord::Rollback
+ end
+ end
+
def destroy
if @storage_location_repository_row.discard
render json: {}
@@ -53,8 +70,21 @@ class StorageLocationRepositoryRowsController < ApplicationController
end
end
+ def actions_toolbar
+ render json: {
+ actions: Toolbars::StorageLocationRepositoryRowsService.new(
+ current_user,
+ items_ids: JSON.parse(params[:items]).map { |i| i['id'] }
+ ).actions
+ }
+ end
+
private
+ def check_storage_locations_enabled
+ render_403 unless StorageLocation.storage_locations_enabled?
+ end
+
def load_storage_location_repository_row
@storage_location_repository_row = StorageLocationRepositoryRow.find(
storage_location_repository_row_params[:id]
@@ -80,10 +110,12 @@ class StorageLocationRepositoryRowsController < ApplicationController
end
def check_read_permissions
- render_403 unless true
+ render_403 unless can_read_storage_location_containers?(current_team)
end
def check_manage_permissions
- render_403 unless true
+ unless can_manage_storage_location_containers?(current_team) && can_read_repository?(@repository_row.repository)
+ render_403
+ end
end
end
diff --git a/app/controllers/storage_locations_controller.rb b/app/controllers/storage_locations_controller.rb
index a6af1aecc..49c0375a1 100644
--- a/app/controllers/storage_locations_controller.rb
+++ b/app/controllers/storage_locations_controller.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
class StorageLocationsController < ApplicationController
- before_action :load_storage_location, only: %i(update destroy)
- before_action :check_read_permissions, only: :index
- before_action :check_manage_permissions, except: :index
- before_action :set_breadcrumbs_items, only: :index
+ before_action :check_storage_locations_enabled, except: :unassign_rows
+ before_action :load_storage_location, only: %i(update destroy duplicate move show available_positions unassign_rows)
+ before_action :check_read_permissions, except: %i(index create tree actions_toolbar)
+ before_action :check_create_permissions, only: :create
+ before_action :check_manage_permissions, only: %i(update destroy duplicate move unassign_rows)
+ before_action :set_breadcrumbs_items, only: %i(index show)
def index
respond_to do |format|
@@ -17,14 +19,17 @@ class StorageLocationsController < ApplicationController
end
end
+ def show; end
+
def update
- @storage_location.image.attach(storage_location_params[:signed_blob_id]) if storage_location_params[:signed_blob_id]
+ @storage_location.image.purge if params[:file_name].blank?
+ @storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
@storage_location.update(storage_location_params)
if @storage_location.save
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
- render json: @storage_location.errors, status: :unprocessable_entity
+ render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
end
end
@@ -33,12 +38,12 @@ class StorageLocationsController < ApplicationController
storage_location_params.merge({ team: current_team, created_by: current_user })
)
- @storage_location.image.attach(storage_location_params[:signed_blob_id]) if storage_location_params[:signed_blob_id]
+ @storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
if @storage_location.save
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
- render json: @storage_location.errors, status: :unprocessable_entity
+ render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
end
end
@@ -46,34 +51,103 @@ class StorageLocationsController < ApplicationController
if @storage_location.discard
render json: {}
else
- render json: { errors: @storage_location.errors.full_messages }, status: :unprocessable_entity
+ render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
end
end
+ def duplicate
+ new_storage_location = @storage_location.duplicate!
+ if new_storage_location
+ render json: new_storage_location, serializer: Lists::StorageLocationSerializer
+ else
+ render json: { errors: :failed }, status: :unprocessable_entity
+ end
+ end
+
+ def move
+ storage_location_destination =
+ if move_params[:destination_storage_location_id] == 'root_storage_location'
+ nil
+ else
+ current_team.storage_locations.find(move_params[:destination_storage_location_id])
+ end
+
+ @storage_location.update!(parent: storage_location_destination)
+
+ render json: { message: I18n.t('storage_locations.index.move_modal.success_flash') }
+ rescue StandardError => e
+ Rails.logger.error e.message
+ Rails.logger.error e.backtrace.join("\n")
+ render json: { error: I18n.t('storage_locations.index.move_modal.error_flash') }, status: :bad_request
+ end
+
+ def tree
+ records = current_team.storage_locations.where(parent: nil, container: [false, params[:container] == 'true'])
+ render json: storage_locations_recursive_builder(records)
+ end
+
+ def available_positions
+ render json: { positions: @storage_location.available_positions }
+ end
+
+ def unassign_rows
+ @storage_location.storage_location_repository_rows.where(id: params[:ids]).discard_all
+
+ render json: { status: :ok }
+ end
+
def actions_toolbar
render json: {
- actions: [] # TODO: Add actions
+ actions:
+ Toolbars::StorageLocationsService.new(
+ current_user,
+ storage_location_ids: JSON.parse(params[:items]).map { |i| i['id'] }
+ ).actions
}
end
private
+ def check_storage_locations_enabled
+ render_403 unless StorageLocation.storage_locations_enabled?
+ end
+
def storage_location_params
- params.permit(:id, :parent_id, :name, :container, :signed_blob_id, :description,
- metadata: { dimensions: [], parent_coordinations: [], display_type: :string })
+ params.permit(:id, :parent_id, :name, :container, :description,
+ metadata: [:display_type, dimensions: [], parent_coordinations: []])
+ end
+
+ def move_params
+ params.permit(:id, :destination_storage_location_id)
end
def load_storage_location
- @storage_location = StorageLocation.where(team: current_team).find(storage_location_params[:id])
+ @storage_location = current_team.storage_locations.find_by(id: storage_location_params[:id])
render_404 unless @storage_location
end
def check_read_permissions
- render_403 unless true
+ if @storage_location.container
+ render_403 unless can_read_storage_location_containers?(current_team)
+ else
+ render_403 unless can_read_storage_locations?(current_team)
+ end
+ end
+
+ def check_create_permissions
+ if storage_location_params[:container]
+ render_403 unless can_create_storage_location_containers?(current_team)
+ else
+ render_403 unless can_create_storage_locations?(current_team)
+ end
end
def check_manage_permissions
- render_403 unless true
+ if @storage_location.container
+ render_403 unless can_manage_storage_location_containers?(current_team)
+ else
+ render_403 unless can_manage_storage_locations?(current_team)
+ end
end
def set_breadcrumbs_items
@@ -89,8 +163,8 @@ class StorageLocationsController < ApplicationController
})
storage_locations = []
- if params[:parent_id]
- location = StorageLocation.where(team: current_team).find_by(id: params[:parent_id])
+ if params[:parent_id] || @storage_location
+ location = (current_team.storage_locations.find_by(id: params[:parent_id]) || @storage_location)
if location
storage_locations.unshift(breadcrumbs_item(location))
while location.parent
@@ -108,4 +182,15 @@ class StorageLocationsController < ApplicationController
url: storage_locations_path(parent_id: location.id)
}
end
+
+ def storage_locations_recursive_builder(storage_locations)
+ storage_locations.map do |storage_location|
+ {
+ storage_location: storage_location,
+ children: storage_locations_recursive_builder(
+ storage_location.storage_locations.where(container: [false, params[:container] == 'true'])
+ )
+ }
+ end
+ end
end
diff --git a/app/controllers/users/settings/user_settings_controller.rb b/app/controllers/users/settings/user_settings_controller.rb
index 2b106bc17..04620a496 100644
--- a/app/controllers/users/settings/user_settings_controller.rb
+++ b/app/controllers/users/settings/user_settings_controller.rb
@@ -17,8 +17,8 @@ module Users
next unless Extends::WHITELISTED_USER_SETTINGS.include?(key.to_s)
case key.to_s
- when 'task_step_states'
- update_task_step_states(data)
+ when 'task_step_states', 'result_states'
+ update_object_states(data, key.to_s)
else
current_user.settings[key] = data
end
@@ -34,18 +34,18 @@ module Users
private
- def update_task_step_states(task_step_states_data)
- current_states = current_user.settings.fetch('task_step_states', {})
+ def update_object_states(object_states_data, object_state_key)
+ current_states = current_user.settings.fetch(object_state_key, {})
- task_step_states_data.each do |step_id, collapsed|
+ object_states_data.each do |object_id, collapsed|
if collapsed
- current_states[step_id] = true
+ current_states[object_id] = true
else
- current_states.delete(step_id)
+ current_states.delete(object_id)
end
end
- current_user.settings['task_step_states'] = current_states
+ current_user.settings[object_state_key] = current_states
end
end
end
diff --git a/app/javascript/packs/vue/storage_locations_container.js b/app/javascript/packs/vue/storage_locations_container.js
new file mode 100644
index 000000000..abf6912c3
--- /dev/null
+++ b/app/javascript/packs/vue/storage_locations_container.js
@@ -0,0 +1,10 @@
+import { createApp } from 'vue/dist/vue.esm-bundler.js';
+import PerfectScrollbar from 'vue3-perfect-scrollbar';
+import StorageLocationsContainer from '../../vue/storage_locations/container.vue';
+import { mountWithTurbolinks } from './helpers/turbolinks.js';
+
+const app = createApp();
+app.component('StorageLocationsContainer', StorageLocationsContainer);
+app.config.globalProperties.i18n = window.I18n;
+app.use(PerfectScrollbar);
+mountWithTurbolinks(app, '#StorageLocationsContainer');
diff --git a/app/javascript/vue/projects/modals/move.vue b/app/javascript/vue/projects/modals/move.vue
index 08eaa66ab..0b92d73a9 100644
--- a/app/javascript/vue/projects/modals/move.vue
+++ b/app/javascript/vue/projects/modals/move.vue
@@ -98,20 +98,19 @@ export default {
if (this.query === '') {
return this.foldersTree;
}
- return this.foldersTree.map((folder) => (
- {
- folder: folder.folder,
- children: folder.children.filter((child) => (
- child.folder.name.toLowerCase().includes(this.query.toLowerCase())
- )),
- }
- )).filter((folder) => (
- folder.folder.name.toLowerCase().includes(this.query.toLowerCase())
- || folder.children.length > 0
- ));
+ return this.filteredFoldersTreeHelper(this.foldersTree);
},
},
methods: {
+ filteredFoldersTreeHelper(foldersTree) {
+ return foldersTree.map(({ folder, children }) => {
+ if (folder.name.toLowerCase().includes(this.query.toLowerCase())) {
+ return { folder, children };
+ }
+ const filteredChildren = this.filteredFoldersTreeHelper(children);
+ return filteredChildren.length ? { folder, children: filteredChildren } : null;
+ }).filter(Boolean);
+ },
selectFolder(folderId) {
this.selectedFolderId = folderId;
},
diff --git a/app/javascript/vue/protocol/container.vue b/app/javascript/vue/protocol/container.vue
index fdd6b6e08..d03b02909 100644
--- a/app/javascript/vue/protocol/container.vue
+++ b/app/javascript/vue/protocol/container.vue
@@ -177,6 +177,7 @@
+ {{ i18n.t("protocols.steps.add_step") }}
+ {{ i18n.t("protocols.steps.add_step") }}
diff --git a/app/javascript/vue/protocol/step.vue b/app/javascript/vue/protocol/step.vue
index 5b1ad50ff..ce3322308 100644
--- a/app/javascript/vue/protocol/step.vue
+++ b/app/javascript/vue/protocol/step.vue
@@ -145,7 +145,19 @@
@attachments:viewMode="changeAttachmentsViewMode"
@attachment:viewMode="updateAttachmentViewMode"/>
+
this.createElement('table', ...args)"
+ @create:checklist="createElement('checklist')"
+ @create:text="createElement('text')"
+ @create:file="openLoadFromComputer"
+ @create:wopi_file="openWopiFileModal"
+ @create:ove_file="openOVEditor"
+ @create:marvinjs_file="openMarvinJsModal($refs.marvinJsButton)"
+ >
+