From 1929959038c1717f24b240f56a25edd16e0f63c6 Mon Sep 17 00:00:00 2001 From: aignatov-bio <47317017+aignatov-bio@users.noreply.github.com> Date: Mon, 9 Aug 2021 21:35:44 +0200 Subject: [PATCH 01/23] Add dropdown for btm filters [SCI-5953] (#3469) --- app/views/repositories/_toolbar_buttons.html.erb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/views/repositories/_toolbar_buttons.html.erb b/app/views/repositories/_toolbar_buttons.html.erb index f0b933e4d..7f2f12b00 100644 --- a/app/views/repositories/_toolbar_buttons.html.erb +++ b/app/views/repositories/_toolbar_buttons.html.erb @@ -85,5 +85,15 @@ <% end %> - + From 958ee92db747738136d8c8600bc174bb24d35610 Mon Sep 17 00:00:00 2001 From: aignatov-bio <47317017+aignatov-bio@users.noreply.github.com> Date: Wed, 18 Aug 2021 13:43:26 +0200 Subject: [PATCH 02/23] Add BMT filters interactions [SCI-5955] (#3478) * Add BMT filters interactions [SCI-5955] * Small fixes [SCI-5955] --- app/assets/javascripts/application.js.erb | 2 + .../repositories/repository_datatable.js | 5 +- app/assets/stylesheets/repositories.scss | 3 +- .../stylesheets/repository/bmt_filters.scss | 37 +++++++++++ app/javascript/packs/vue/bmt_filter.js | 20 ++++++ app/javascript/vue/bmt_filter/container.vue | 61 +++++++++++++++++++ app/javascript/vue/bmt_filter/filter.vue | 20 ++++++ .../repositories/_toolbar_buttons.html.erb | 4 +- app/views/repositories/show.html.erb | 3 + config/locales/en.yml | 5 ++ package.json | 4 +- 11 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 app/assets/stylesheets/repository/bmt_filters.scss create mode 100644 app/javascript/packs/vue/bmt_filter.js create mode 100644 app/javascript/vue/bmt_filter/container.vue create mode 100644 app/javascript/vue/bmt_filter/filter.vue diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 3daeec359..b2f1ff666 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -283,3 +283,5 @@ $(window).scroll(function() { scroll_function(); }) }) + +window.I18n = I18n diff --git a/app/assets/javascripts/repositories/repository_datatable.js b/app/assets/javascripts/repositories/repository_datatable.js index 7ff8bb36a..74c68cf21 100644 --- a/app/assets/javascripts/repositories/repository_datatable.js +++ b/app/assets/javascripts/repositories/repository_datatable.js @@ -1,7 +1,7 @@ /* globals I18n _ SmartAnnotation FilePreviewModal animateSpinner DataTableHelpers HelperModule RepositoryDatatableRowEditor prepareRepositoryHeaderForExport - initAssignedTasksDropdown + initAssignedTasksDropdown initBMTFilter */ //= require jquery-ui/widgets/sortable @@ -268,6 +268,7 @@ var RepositoryDatatable = (function(global) { $.getJSON($(TABLE_ID).data('toolbar-url'), (data) => { $('#toolbarButtonsDatatable').remove(); $(data.html).appendTo('div.toolbar'); + initBMTFilter(); }); TABLE.ajax.reload(null, false); @@ -551,6 +552,7 @@ var RepositoryDatatable = (function(global) { // Append buttons to inner toolbar in the table $.getJSON($(TABLE_ID).data('toolbar-url'), (data) => { $(data.html).appendTo('div.toolbar'); + initBMTFilter(); }); $('div.toolbar-filter-buttons').prependTo('div.filter-container'); @@ -577,6 +579,7 @@ var RepositoryDatatable = (function(global) { }); initAssignedTasksDropdown(TABLE_ID); + } }); diff --git a/app/assets/stylesheets/repositories.scss b/app/assets/stylesheets/repositories.scss index 3e3af7fc9..3535b8cd9 100644 --- a/app/assets/stylesheets/repositories.scss +++ b/app/assets/stylesheets/repositories.scss @@ -153,13 +153,12 @@ flex-wrap: nowrap; height: 6em; left: var(--repository-sidebar-margin); - overflow: hidden; padding: 1em 2em 0; position: fixed; top: calc(4em + var(--navbar-height)); transition: .4s $timing-function-sharp; width: calc(100% - var(--repository-sidebar-margin)); - z-index: 90; + z-index: 100; .filter-container { flex-shrink: 0; diff --git a/app/assets/stylesheets/repository/bmt_filters.scss b/app/assets/stylesheets/repository/bmt_filters.scss new file mode 100644 index 000000000..eb17a0ceb --- /dev/null +++ b/app/assets/stylesheets/repository/bmt_filters.scss @@ -0,0 +1,37 @@ +#bmtFilterContainer { + min-width: 300px; + z-index: 100; + + .header { + align-items: center; + border-bottom: $border-tertiary; + display: flex; + padding: .5em 1em; + + .title { + @include font-h2; + margin-right: auto; + } + } + + .filter-element { + align-items: center; + display: flex; + } + + .filters-list { + border-top: $border-tertiary; + padding: .5em 1em; + } + + .footer { + align-items: center; + border-top: $border-tertiary; + display: flex; + padding: .5em 1em; + + .add-filter { + margin-right: auto; + } + } +} diff --git a/app/javascript/packs/vue/bmt_filter.js b/app/javascript/packs/vue/bmt_filter.js new file mode 100644 index 000000000..a1938e366 --- /dev/null +++ b/app/javascript/packs/vue/bmt_filter.js @@ -0,0 +1,20 @@ +import TurbolinksAdapter from 'vue-turbolinks'; +import Vue from 'vue/dist/vue.esm'; +import FilterContainer from '../../vue/bmt_filter/container.vue'; + +Vue.use(TurbolinksAdapter); + + +window.initBMTFilter = () => { + const app = new Vue({ + el: '#bmtFilterContainer', + components: { + 'filter-container': FilterContainer + } + }); + + $('#bmtFilterContainer').on('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + }); +}; diff --git a/app/javascript/vue/bmt_filter/container.vue b/app/javascript/vue/bmt_filter/container.vue new file mode 100644 index 000000000..9793d94b3 --- /dev/null +++ b/app/javascript/vue/bmt_filter/container.vue @@ -0,0 +1,61 @@ + + + diff --git a/app/javascript/vue/bmt_filter/filter.vue b/app/javascript/vue/bmt_filter/filter.vue new file mode 100644 index 000000000..7c882edb3 --- /dev/null +++ b/app/javascript/vue/bmt_filter/filter.vue @@ -0,0 +1,20 @@ + + + diff --git a/app/views/repositories/_toolbar_buttons.html.erb b/app/views/repositories/_toolbar_buttons.html.erb index 7f2f12b00..b6f9c8e49 100644 --- a/app/views/repositories/_toolbar_buttons.html.erb +++ b/app/views/repositories/_toolbar_buttons.html.erb @@ -94,6 +94,8 @@ > - + diff --git a/app/views/repositories/show.html.erb b/app/views/repositories/show.html.erb index 6505ebbe8..61a2d6046 100644 --- a/app/views/repositories/show.html.erb +++ b/app/views/repositories/show.html.erb @@ -171,6 +171,7 @@ <%= render partial: 'repository_columns/manage_column_modal', locals: { my_module_page: false } %> +<%= javascript_pack_tag 'vue/bmt_filter' %> <%= javascript_include_tag 'repositories/edit' %> <%= javascript_include_tag 'repositories/repository_datatable' %> <%= javascript_include_tag "repositories/show" %> @@ -179,6 +180,8 @@ <%= javascript_pack_tag 'pdfjs/pdf_js' %> <%= stylesheet_pack_tag 'pdfjs/pdf_js_styles' %> + + diff --git a/config/locales/en.yml b/config/locales/en.yml index ddceb65ff..1a015c643 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1364,6 +1364,11 @@ en: no_archived_items: "No archived items here" no_archived_items_matched: "No archived items matched your search request" + bmt_search: + title: "Filters" + clear_all: "Clear all" + add_filter: "Add filter" + apply: "Apply" table: id: 'ID' assigned: "Assigned" diff --git a/package.json b/package.json index e7106f331..0736d6f1b 100644 --- a/package.json +++ b/package.json @@ -83,8 +83,8 @@ "postcss-smart-import": "^0.7.6", "precss": "^2.0.0", "prop-types": "^15.7.2", - "rails-erb-loader": "^5.4.2", "react": "^16.8.6", + "rails-erb-loader": "^5.4.2", "react-bootstrap": "^0.31.5", "react-bootstrap-table": "^4.3.1", "react-bootstrap-timezone-picker": "^1.0.12", @@ -113,10 +113,10 @@ "tui-image-editor": "git://github.com/biosistemika/tui.image-editor", "twemoji": "^12.1.4", "typeface-lato": "^0.0.75", + "vue": "^2.6.11", "vue-loader": "^15.9.1", "vue-template-compiler": "^2.6.12", "vue-turbolinks": "^2.2.1", - "vue": "^2.6.11", "webpack": "^4.35.2", "webpack-cli": "^3.3.5", "webpack-manifest-plugin": "^1.3.2", From 0b74f34de7966b0ad0b03c898b4dacbfa006bee8 Mon Sep 17 00:00:00 2001 From: Oleksii Kriuchykhin Date: Fri, 13 Aug 2021 14:07:01 +0200 Subject: [PATCH 03/23] Add external IDs to repositories [SCI-5990] --- app/controllers/repositories_controller.rb | 2 +- app/helpers/repository_datatable_helper.rb | 54 ++++++++++--------- app/models/linked_repository.rb | 11 ++++ app/models/repository.rb | 4 +- app/models/repository_base.rb | 19 +++++++ app/models/repository_column.rb | 2 +- app/models/repository_snapshot.rb | 4 +- app/services/repository_datatable_service.rb | 12 ++--- ...itory_table_state_column_update_service.rb | 4 +- .../repository_table_state_service.rb | 6 +-- .../_full_view_snapshot_table.html.erb | 4 +- .../repositories/_full_view_table.html.erb | 4 +- .../repositories/_repository_table.html.erb | 7 ++- config/initializers/extends.rb | 2 + config/locales/en.yml | 1 + ...2095254_add_external_id_to_repositories.rb | 19 +++++++ lib/sti_preload.rb | 27 ++++++++++ .../repository_rows_controller_spec.rb | 2 +- 18 files changed, 136 insertions(+), 48 deletions(-) create mode 100644 app/models/linked_repository.rb create mode 100644 db/migrate/20210812095254_add_external_id_to_repositories.rb create mode 100644 lib/sti_preload.rb diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 5ff2ca8a4..fc8543baa 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -27,7 +27,7 @@ class RepositoriesController < ApplicationController def index respond_to do |format| format.html do - render 'empty_index' if Repository.accessible_by_teams(current_team).blank? + render 'empty_index' if @repositories.blank? end format.json do render json: prepare_repositories_datatable(@repositories, current_team, params) diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb index bdaa44103..28526d08e 100644 --- a/app/helpers/repository_datatable_helper.rb +++ b/app/helpers/repository_datatable_helper.rb @@ -5,18 +5,15 @@ module RepositoryDatatableHelper def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {}) repository_rows.map do |record| - row = { - 'DT_RowId': record.id, - 'DT_RowAttr': { 'data-state': row_style(record) }, - '1': assigned_row(record), - '2': record.code, - '3': escape_input(record.name), - '4': I18n.l(record.created_at, format: :full), - '5': escape_input(record.created_by.full_name), - '6': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''), - '7': escape_input(record.archived_by&.full_name), - 'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(repository, record) - } + row = if repository.is_a?(LinkedRepository) + linked_repository_default_columns(record) + else + repository_default_columns(record) + end + + row['DT_RowId'] = record.id + row['DT_RowAttr'] = { 'data-state': row_style(record) } + row['recordInfoUrl'] = Rails.application.routes.url_helpers.repository_repository_row_path(repository, record) unless options[:view_mode] row['recordUpdateUrl'] = @@ -84,20 +81,29 @@ module RepositoryDatatableHelper can_manage_repository_rows?(repository) end - def default_table_order_as_js_array - Constants::REPOSITORY_TABLE_DEFAULT_STATE['order'].to_json + def repository_default_columns(record) + { + '1': assigned_row(record), + '2': record.code, + '3': escape_input(record.name), + '4': I18n.l(record.created_at, format: :full), + '5': escape_input(record.created_by.full_name), + '6': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''), + '7': escape_input(record.archived_by&.full_name) + } end - def default_table_columns - Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].to_json - end - - def default_snapshot_table_order_as_js_array - Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['order'].to_json - end - - def default_snapshot_table_columns - Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'].to_json + def linked_repository_default_columns(record) + { + '1': assigned_row(record), + '2': escape_input(record.external_id), + '3': record.code, + '4': escape_input(record.name), + '5': I18n.l(record.created_at, format: :full), + '6': escape_input(record.created_by.full_name), + '7': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''), + '8': escape_input(record.archived_by&.full_name) + } end def display_cell_value(cell, team) diff --git a/app/models/linked_repository.rb b/app/models/linked_repository.rb new file mode 100644 index 000000000..84e4bbe00 --- /dev/null +++ b/app/models/linked_repository.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class LinkedRepository < Repository + def default_table_state + state = Constants::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup + state['order'] = [[3, 'asc']] + state['ColReorder'] << state['ColReorder'].length + state['columns'].insert(1, Constants::REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE) + state + end +end diff --git a/app/models/repository.rb b/app/models/repository.rb index 42fca0d88..5b95b85e2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -108,8 +108,8 @@ class Repository < RepositoryBase end end - def default_columns_count - Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length + def default_table_state + Constants::REPOSITORY_TABLE_DEFAULT_STATE end def i_shared?(team) diff --git a/app/models/repository_base.rb b/app/models/repository_base.rb index 715710107..35c924a85 100644 --- a/app/models/repository_base.rb +++ b/app/models/repository_base.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class RepositoryBase < ApplicationRecord + require 'sti_preload' + + include StiPreload include Discard::Model self.table_name = 'repositories' @@ -32,6 +35,22 @@ class RepositoryBase < ApplicationRecord cell_includes end + def default_table_state + raise NotImplementedError + end + + def default_table_columns + default_table_state['columns'].to_json + end + + def default_columns_count + default_table_state['columns'].length + end + + def default_table_order_as_js_array + default_table_state['order'].to_json + end + def destroy_discarded(discarded_by_id = nil) self.discarded_by_id = discarded_by_id destroy diff --git a/app/models/repository_column.rb b/app/models/repository_column.rb index 1df89b99b..8d5f071a4 100644 --- a/app/models/repository_column.rb +++ b/app/models/repository_column.rb @@ -55,7 +55,7 @@ class RepositoryColumn < ApplicationRecord # Calculate old_column_index - this can only be done before # record is deleted when we still have its index old_column_index = ( - Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length + + repository.default_columns_count + repository.repository_columns .order(id: :asc) .pluck(:id) diff --git a/app/models/repository_snapshot.rb b/app/models/repository_snapshot.rb index 620fc5aed..1c4c35f51 100644 --- a/app/models/repository_snapshot.rb +++ b/app/models/repository_snapshot.rb @@ -45,8 +45,8 @@ class RepositorySnapshot < RepositoryBase repository_snapshot.reload end - def default_columns_count - Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'].length + def default_table_state + Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE end def assigned_rows(my_module) diff --git a/app/services/repository_datatable_service.rb b/app/services/repository_datatable_service.rb index efa66d9b8..175370cc7 100644 --- a/app/services/repository_datatable_service.rb +++ b/app/services/repository_datatable_service.rb @@ -78,11 +78,10 @@ class RepositoryDatatableService if search_value.present? matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value) - repository_row_matches = repository_rows - .where_attributes_like( - ['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL], - search_value - ) + repository_row_search_fileds = ['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL] + repository_row_search_fileds << 'repository_rows.external_id' if @repository.is_a?(LinkedRepository) + + repository_row_matches = repository_rows.where_attributes_like(repository_row_search_fileds, search_value) results = repository_rows.where(id: repository_row_matches) results = results.or(repository_rows.where(id: matched_by_user)) @@ -121,8 +120,9 @@ class RepositoryDatatableService 'repository_rows.created_at', 'users.full_name', 'repository_rows.archived_on', - 'archived_bies_repository_rows.full_name', + 'archived_bies_repository_rows.full_name' ] + array.insert(1, 'repository_rows.external_id') if @repository.is_a?(LinkedRepository) @repository.repository_columns.count.times do array << 'repository_cell.value' end diff --git a/app/services/repository_table_state_column_update_service.rb b/app/services/repository_table_state_column_update_service.rb index 302e35d79..dcf3d0432 100644 --- a/app/services/repository_table_state_column_update_service.rb +++ b/app/services/repository_table_state_column_update_service.rb @@ -9,7 +9,7 @@ class RepositoryTableStateColumnUpdateService def update_states_with_new_column(repository) raise ArgumentError, 'repository is empty' if repository.blank? - columns_num = Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length + repository.repository_columns.count + columns_num = repository.default_columns_count + repository.repository_columns.count RepositoryTableState.where( repository: repository ).find_each do |table_state| @@ -49,7 +49,7 @@ class RepositoryTableStateColumnUpdateService if state.dig('order', 0, 0).to_i == old_column_index # Fallback to default order if user had table ordered by # the deleted column - state['order'] = Constants::REPOSITORY_TABLE_DEFAULT_STATE['order'] + state['order'] = repository.default_table_state['order'] elsif state.dig('order', 0, 0).to_i > old_column_index state['order'][0][0] -= 1 end diff --git a/app/services/repository_table_state_service.rb b/app/services/repository_table_state_service.rb index 44122b464..2fd722c40 100644 --- a/app/services/repository_table_state_service.rb +++ b/app/services/repository_table_state_service.rb @@ -26,7 +26,7 @@ class RepositoryTableStateService def update_state(state) saved_state = load_state - state['order'] = Constants::REPOSITORY_TABLE_DEFAULT_STATE['order'] if state.dig('order', 0, 0).to_i < 1 + state['order'] = @repository.default_table_state['order'] if state.dig('order', 0, 0).to_i < 1 return if saved_state.state.except('time') == state.except('time') @@ -47,10 +47,10 @@ class RepositoryTableStateService private def generate_default_state - default_columns_num = Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length + default_columns_num = @repository.default_columns_count # This state should be strings-only - state = Constants::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup + state = @repository.default_table_state.deep_dup @repository.repository_columns.each_with_index do |_, index| real_index = default_columns_num + index state['columns'][real_index] = Constants::REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE diff --git a/app/views/my_modules/repositories/_full_view_snapshot_table.html.erb b/app/views/my_modules/repositories/_full_view_snapshot_table.html.erb index 0df8097c9..d18650e48 100644 --- a/app/views/my_modules/repositories/_full_view_snapshot_table.html.erb +++ b/app/views/my_modules/repositories/_full_view_snapshot_table.html.erb @@ -5,8 +5,8 @@ data-repository-name="<%= @repository_snapshot.name %>" data-repository-snapshot-created="<%= l(@repository_snapshot.created_at, format: :full) %>" data-assigned-items-count="<%= @repository_snapshot.repository_rows.count %>" - data-default-order="<%= default_snapshot_table_order_as_js_array %>" - data-default-table-columns="<%= default_snapshot_table_columns %>" + data-default-order="<%= @repository_snapshot.default_table_order_as_js_array %>" + data-default-table-columns="<%= @repository_snapshot.default_table_columns %>" data-load-state-url="<%= repository_load_table_state_path(@repository_snapshot) %>" data-export-url="<%= export_repository_snapshot_my_module_repository_snapshot_path(@my_module, @repository_snapshot) %>" data-versions-sidebar-url="<%= full_view_sidebar_my_module_repository_snapshots_path(@my_module, @repository_snapshot.parent_id) %>" diff --git a/app/views/my_modules/repositories/_full_view_table.html.erb b/app/views/my_modules/repositories/_full_view_table.html.erb index 9b7873577..bd999ff37 100644 --- a/app/views/my_modules/repositories/_full_view_table.html.erb +++ b/app/views/my_modules/repositories/_full_view_table.html.erb @@ -2,8 +2,8 @@ data-id="<%= @repository.id %>" data-type="live" data-source="<%= index_dt_my_module_repository_path(@my_module, @repository) %>" - data-default-order="<%= default_table_order_as_js_array %>" - data-default-table-columns="<%= default_table_columns %>" + data-default-order="<%= @repository.default_table_order_as_js_array %>" + data-default-table-columns="<%= @repository.default_table_columns %>" data-repository-name="<%= @repository.name %>" data-assigned-items-count="<%= @my_module.repository_rows_count(@repository) %>" data-load-state-url="<%= repository_load_table_state_path(@repository) %>" diff --git a/app/views/repositories/_repository_table.html.erb b/app/views/repositories/_repository_table.html.erb index fe11fa6c9..a3a499d1f 100644 --- a/app/views/repositories/_repository_table.html.erb +++ b/app/views/repositories/_repository_table.html.erb @@ -20,8 +20,8 @@ data-columns-delete-text="<%= I18n.t('repositories.columns_delete') %>" data-available-columns="<%= repository_available_columns_path(repository) %>" data-columns-changed="<%= I18n.t('repositories.columns_changed') %>" - data-default-order="<%= default_table_order_as_js_array %>" - data-default-table-columns="<%= default_table_columns %>" + data-default-order="<%= @repository.default_table_order_as_js_array %>" + data-default-table-columns="<%= @repository.default_table_columns %>" data-editable="<%= can_manage_repository_rows?(repository) %>" data-snapshot-provisioning="<%= @snapshot_provisioning %>" data-status-url="<%= repository_status_path(@repository) %>"> @@ -32,6 +32,9 @@ <%= t("repositories.table.assigned") %> + <% if @repository.is_a?(LinkedRepository) %> + <%= t('repositories.table.external_id') %> + <% end %> <%= t("repositories.table.id") %> <%= t("repositories.table.row_name") %> <%= t("repositories.table.added_on") %> diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb index 7f70cb9f7..0b715b08c 100644 --- a/config/initializers/extends.rb +++ b/config/initializers/extends.rb @@ -386,4 +386,6 @@ class Extends delete_individual_signature_request delete_group_signature_request ) + + STI_PRELOAD_CLASSES = %w(LinkedRepository) end diff --git a/config/locales/en.yml b/config/locales/en.yml index 1a015c643..303bab0fc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1371,6 +1371,7 @@ en: apply: "Apply" table: id: 'ID' + external_id: 'External ID' assigned: "Assigned" assigned_search: 'Search...' assigned_tooltip: "%{tasks} tasks in %{experiments} experiments, %{projects} projects" diff --git a/db/migrate/20210812095254_add_external_id_to_repositories.rb b/db/migrate/20210812095254_add_external_id_to_repositories.rb new file mode 100644 index 000000000..68d01cb46 --- /dev/null +++ b/db/migrate/20210812095254_add_external_id_to_repositories.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require File.expand_path('app/helpers/database_helper') + +class AddExternalIdToRepositories < ActiveRecord::Migration[6.1] + include DatabaseHelper + + def up + add_column :repository_rows, :external_id, :string, null: true + add_index :repository_rows, :external_id, unique: true, name: 'unique_index_repository_rows_on_external_id' + add_gin_index_without_tags(:repository_rows, :external_id) + end + + def down + remove_index :repository_rows, name: 'index_repository_rows_on_external_id' + remove_index :repository_rows, :external_id, unique: true, name: 'unique_index_repository_rows_on_external_id' + remove_column :repository_rows, :external_id, :string, null: true + end +end diff --git a/lib/sti_preload.rb b/lib/sti_preload.rb new file mode 100644 index 000000000..08342cf69 --- /dev/null +++ b/lib/sti_preload.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module StiPreload + unless Rails.application.config.eager_load + extend ActiveSupport::Concern + + included do + cattr_accessor :preloaded, instance_accessor: false + end + + class_methods do + def descendants + preload_sti unless preloaded + super + end + + def preload_sti + Extends::STI_PRELOAD_CLASSES.each do |type| + logger.debug("Preloading STI type #{type}") + type.constantize + end + + self.preloaded = true + end + end + end +end diff --git a/spec/controllers/repository_rows_controller_spec.rb b/spec/controllers/repository_rows_controller_spec.rb index 10d4bf671..e7a6e9c4d 100644 --- a/spec/controllers/repository_rows_controller_spec.rb +++ b/spec/controllers/repository_rows_controller_spec.rb @@ -13,7 +13,7 @@ describe RepositoryRowsController, type: :controller do RepositoryTableState.create( repository: repository, user: user, - state: Constants::REPOSITORY_TABLE_DEFAULT_STATE + state: repository.default_teble_state ) end let!(:repository_row) do From 9a87bad740ad7935107f7a329f6e743d8fd191da Mon Sep 17 00:00:00 2001 From: Martin Artnik Date: Fri, 20 Aug 2021 11:01:21 +0200 Subject: [PATCH 04/23] Implement filters and JSON building [SCI-5956] --- .../stylesheets/repository/bmt_filters.scss | 50 ++++++++++++++-- app/javascript/.eslintrc.json | 1 + app/javascript/packs/vue/bmt_filter.js | 7 +-- app/javascript/vue/bmt_filter/container.vue | 47 +++++++++++---- app/javascript/vue/bmt_filter/filter.vue | 58 +++++++++++++++++- .../filters/additionalDataFilter.vue | 3 + .../bmt_filter/filters/entityTypeFilter.vue | 25 ++++++++ .../bmt_filter/filters/fullSequenceFilter.vue | 42 +++++++++++++ .../monomerSubstructureSearchFilter.vue | 25 ++++++++ .../bmt_filter/filters/monomerTypeFilter.vue | 25 ++++++++ .../bmt_filter/filters/subsequenceFilter.vue | 42 +++++++++++++ .../filters/variantSequenceFilter.vue | 59 +++++++++++++++++++ .../vue/bmt_filter/mixins/filter.js | 19 ++++++ config/locales/en.yml | 31 ++++++++++ 14 files changed, 410 insertions(+), 24 deletions(-) create mode 100644 app/javascript/vue/bmt_filter/filters/additionalDataFilter.vue create mode 100644 app/javascript/vue/bmt_filter/filters/entityTypeFilter.vue create mode 100644 app/javascript/vue/bmt_filter/filters/fullSequenceFilter.vue create mode 100644 app/javascript/vue/bmt_filter/filters/monomerSubstructureSearchFilter.vue create mode 100644 app/javascript/vue/bmt_filter/filters/monomerTypeFilter.vue create mode 100644 app/javascript/vue/bmt_filter/filters/subsequenceFilter.vue create mode 100644 app/javascript/vue/bmt_filter/filters/variantSequenceFilter.vue create mode 100644 app/javascript/vue/bmt_filter/mixins/filter.js diff --git a/app/assets/stylesheets/repository/bmt_filters.scss b/app/assets/stylesheets/repository/bmt_filters.scss index eb17a0ceb..de4a60401 100644 --- a/app/assets/stylesheets/repository/bmt_filters.scss +++ b/app/assets/stylesheets/repository/bmt_filters.scss @@ -1,12 +1,36 @@ #bmtFilterContainer { - min-width: 300px; + min-width: 422px; z-index: 100; + label { + font-weight: normal; + } + + .form-group { + display: block; + width: 100%; + + .form-control, + .form-select { + margin-bottom: 21px; + width: 100%; + } + } + + .checkbox-label { + vertical-align: text-bottom; + } + + .filter-list-notice { + color: $color-silver-chalice; + padding: 16px; + } + .header { align-items: center; border-bottom: $border-tertiary; display: flex; - padding: .5em 1em; + padding: 0 1em; .title { @include font-h2; @@ -16,19 +40,37 @@ .filter-element { align-items: center; + border-bottom: $border-tertiary; display: flex; + padding: 15px 52px 15px 15px; + position: relative; + } + + .filter-remove { + padding: 0; + position: absolute; + right: 16px; + top: 16px; + + .btn { + height: 16px; + padding: 0; + width: 16px; + } + + .btn:hover { + background: $color-white; + } } .filters-list { border-top: $border-tertiary; - padding: .5em 1em; } .footer { align-items: center; border-top: $border-tertiary; display: flex; - padding: .5em 1em; .add-filter { margin-right: auto; diff --git a/app/javascript/.eslintrc.json b/app/javascript/.eslintrc.json index 602de750e..c3295ac1b 100644 --- a/app/javascript/.eslintrc.json +++ b/app/javascript/.eslintrc.json @@ -16,6 +16,7 @@ "jsx": true } }, + "ignorePatterns": ["*.vue", "**/mixins/*.js"], "rules": { "import/extensions": "off", "import/no-unresolved": "off", diff --git a/app/javascript/packs/vue/bmt_filter.js b/app/javascript/packs/vue/bmt_filter.js index a1938e366..2ac28efe6 100644 --- a/app/javascript/packs/vue/bmt_filter.js +++ b/app/javascript/packs/vue/bmt_filter.js @@ -3,7 +3,7 @@ import Vue from 'vue/dist/vue.esm'; import FilterContainer from '../../vue/bmt_filter/container.vue'; Vue.use(TurbolinksAdapter); - +Vue.prototype.i18n = window.I18n; window.initBMTFilter = () => { const app = new Vue({ @@ -13,8 +13,5 @@ window.initBMTFilter = () => { } }); - $('#bmtFilterContainer').on('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - }); + $('#bmtFilterContainer').on('click', (e) => e.stopPropagation()); }; diff --git a/app/javascript/vue/bmt_filter/container.vue b/app/javascript/vue/bmt_filter/container.vue index 9793d94b3..44db6fba2 100644 --- a/app/javascript/vue/bmt_filter/container.vue +++ b/app/javascript/vue/bmt_filter/container.vue @@ -10,7 +10,16 @@
- +
+

{{ i18n.t('repositories.show.bmt_search.no_filters') }}

+
+
-

{{ i18n.t('repositories.show.bmt_search.no_filters') }}

+ {{ i18n.t('repositories.show.bmt_search.no_filters') }}
Date: Fri, 20 Aug 2021 13:14:18 +0200 Subject: [PATCH 06/23] Add BMT inventory model [SCI-6009] --- app/helpers/repository_datatable_helper.rb | 16 +++++++--- app/models/bmt_repository.rb | 33 ++++++++++++++++++++ app/models/linked_repository.rb | 17 ++++++++++ app/models/repository.rb | 16 ++++++++++ app/services/repository_datatable_service.rb | 22 +++---------- config/initializers/extends.rb | 2 +- config/locales/en.yml | 1 + 7 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 app/models/bmt_repository.rb diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb index 28526d08e..ebf04ad35 100644 --- a/app/helpers/repository_datatable_helper.rb +++ b/app/helpers/repository_datatable_helper.rb @@ -5,11 +5,7 @@ module RepositoryDatatableHelper def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {}) repository_rows.map do |record| - row = if repository.is_a?(LinkedRepository) - linked_repository_default_columns(record) - else - repository_default_columns(record) - end + row = public_send("#{repository.class.name.underscore}_default_columns", record) row['DT_RowId'] = record.id row['DT_RowAttr'] = { 'data-state': row_style(record) } @@ -106,6 +102,16 @@ module RepositoryDatatableHelper } end + def bmt_repository_default_columns(record) + { + '1': assigned_row(record), + '2': escape_input(record.external_id), + '3': record.code, + '4': escape_input(record.name), + '5': I18n.l(record.created_at, format: :full) + } + end + def display_cell_value(cell, team) serializer_class = "RepositoryDatatable::#{cell.repository_column.data_type}Serializer".constantize serializer_class.new( diff --git a/app/models/bmt_repository.rb b/app/models/bmt_repository.rb new file mode 100644 index 000000000..dfe7fa889 --- /dev/null +++ b/app/models/bmt_repository.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class BmtRepository < LinkedRepository + before_create :enforce_singleton + + def default_table_state + state = Constants::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup + state['order'] = [[3, 'asc']] + state['ColReorder'] << state['ColReorder'].length + state['columns'].pop(2) + state + end + + def default_sortable_columns + [ + 'assigned', + 'repository_rows.external_id', + 'repository_rows.id', + 'repository_rows.name', + 'repository_rows.created_at' + ] + end + + def default_search_fileds + super - ['users.full_name'] + end + + private + + def enforce_singleton + raise ActiveRecord::RecordNotSaved, I18n.t('repositories.bmt_singleton_error') if self.class.any? + end +end diff --git a/app/models/linked_repository.rb b/app/models/linked_repository.rb index 84e4bbe00..ac97775ec 100644 --- a/app/models/linked_repository.rb +++ b/app/models/linked_repository.rb @@ -8,4 +8,21 @@ class LinkedRepository < Repository state['columns'].insert(1, Constants::REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE) state end + + def default_sortable_columns + [ + 'assigned', + 'repository_rows.external_id', + 'repository_rows.id', + 'repository_rows.name', + 'repository_rows.created_at', + 'users.full_name', + 'repository_rows.archived_on', + 'archived_bies_repository_rows.full_name' + ] + end + + def default_search_fileds + super << 'repository_rows.external_id' + end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 5b95b85e2..e86d375a2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -112,6 +112,22 @@ class Repository < RepositoryBase Constants::REPOSITORY_TABLE_DEFAULT_STATE end + def default_sortable_columns + [ + 'assigned', + 'repository_rows.id', + 'repository_rows.name', + 'repository_rows.created_at', + 'users.full_name', + 'repository_rows.archived_on', + 'archived_bies_repository_rows.full_name' + ] + end + + def default_search_fileds + ['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL, 'users.full_name'] + end + def i_shared?(team) shared_with_anybody? && self.team == team end diff --git a/app/services/repository_datatable_service.rb b/app/services/repository_datatable_service.rb index 175370cc7..07cb73f12 100644 --- a/app/services/repository_datatable_service.rb +++ b/app/services/repository_datatable_service.rb @@ -76,14 +76,11 @@ class RepositoryDatatableService end if search_value.present? - matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value) - - repository_row_search_fileds = ['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL] - repository_row_search_fileds << 'repository_rows.external_id' if @repository.is_a?(LinkedRepository) - - repository_row_matches = repository_rows.where_attributes_like(repository_row_search_fileds, search_value) + if @repository.default_search_fileds.include?('users.full_name') + repository_rows = repository_rows.joins(:created_by) + end + repository_row_matches = repository_rows.where_attributes_like(@repository.default_search_fileds, search_value) results = repository_rows.where(id: repository_row_matches) - results = results.or(repository_rows.where(id: matched_by_user)) data_types = @repository.repository_columns.pluck(:data_type).uniq @@ -113,16 +110,7 @@ class RepositoryDatatableService end def build_sortable_columns - array = [ - 'assigned', - 'repository_rows.id', - 'repository_rows.name', - 'repository_rows.created_at', - 'users.full_name', - 'repository_rows.archived_on', - 'archived_bies_repository_rows.full_name' - ] - array.insert(1, 'repository_rows.external_id') if @repository.is_a?(LinkedRepository) + array = @repository.default_sortable_columns @repository.repository_columns.count.times do array << 'repository_cell.value' end diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb index 0b715b08c..ea56545cc 100644 --- a/config/initializers/extends.rb +++ b/config/initializers/extends.rb @@ -387,5 +387,5 @@ class Extends delete_group_signature_request ) - STI_PRELOAD_CLASSES = %w(LinkedRepository) + STI_PRELOAD_CLASSES = %w(LinkedRepository BmtRepository) end diff --git a/config/locales/en.yml b/config/locales/en.yml index 38f9c3eaa..c91435a2a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1280,6 +1280,7 @@ en: i_shared: "Shared inventory (owned by your Team)" shared_edit: "Shared inventory (owned by %{team_name}). You can edit." shared_read: "Shared inventory (owned by %{team_name}). You can view." + bmt_singleton_error: "Only one BMT inventory can be present." index: head_title: "Inventories" title: "Inventories" From 90d4a5f7d1bd4c17c949e268bac18dd3295ebfb2 Mon Sep 17 00:00:00 2001 From: aignatov-bio <47317017+aignatov-bio@users.noreply.github.com> Date: Tue, 24 Aug 2021 14:09:48 +0200 Subject: [PATCH 07/23] Add save bmt filters front end [SCI-5962] (#3505) --- .../stylesheets/repository/bmt_filters.scss | 6 ++++ app/javascript/packs/vue/bmt_filter.js | 23 +++++++++++-- app/javascript/vue/bmt_filter/container.vue | 29 ++++++++++++++--- .../vue/bmt_filter/saved_filter.vue | 18 +++++++++++ .../_save_bmt_filter_modal.html.erb | 25 +++++++++++++++ .../repositories/_toolbar_buttons.html.erb | 32 ++++++++++++------- app/views/repositories/show.html.erb | 1 + config/locales/en.yml | 2 ++ 8 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 app/javascript/vue/bmt_filter/saved_filter.vue create mode 100644 app/views/repositories/_save_bmt_filter_modal.html.erb diff --git a/app/assets/stylesheets/repository/bmt_filters.scss b/app/assets/stylesheets/repository/bmt_filters.scss index bac2df591..3f50c589e 100644 --- a/app/assets/stylesheets/repository/bmt_filters.scss +++ b/app/assets/stylesheets/repository/bmt_filters.scss @@ -68,4 +68,10 @@ margin-right: auto; } } + + .savedFilterContainer { + .saved-filter-element { + display: flex; + } + } } diff --git a/app/javascript/packs/vue/bmt_filter.js b/app/javascript/packs/vue/bmt_filter.js index 2ac28efe6..0ada30b44 100644 --- a/app/javascript/packs/vue/bmt_filter.js +++ b/app/javascript/packs/vue/bmt_filter.js @@ -6,12 +6,31 @@ Vue.use(TurbolinksAdapter); Vue.prototype.i18n = window.I18n; window.initBMTFilter = () => { - const app = new Vue({ + const bmtFilterContainer = new Vue({ el: '#bmtFilterContainer', + data: () => { + return { + saved_filters: [] + }; + }, components: { 'filter-container': FilterContainer } }); - $('#bmtFilterContainer').on('click', (e) => e.stopPropagation()); + + $( "#saveBmtFilterForm" ).off().submit(function(e) { + let filterName = $(this).find('input#filter_name').val(); + bmtFilterContainer.saved_filters.push({ + id: bmtFilterContainer.saved_filters.length, + filter_name: filterName, + filters: [] + }); + }); + }; + +$('.repository-show').on('click', '.open-save-bmt-modal', () => { + $('#modalSaveBmtFilter').modal('show') +}) + diff --git a/app/javascript/vue/bmt_filter/container.vue b/app/javascript/vue/bmt_filter/container.vue index 324993f64..defae712e 100644 --- a/app/javascript/vue/bmt_filter/container.vue +++ b/app/javascript/vue/bmt_filter/container.vue @@ -1,8 +1,19 @@