From 02f039ffed45f14ea82939b944d255526f9a451f Mon Sep 17 00:00:00 2001 From: ajugo Date: Mon, 5 Dec 2022 10:30:20 +0100 Subject: [PATCH] Implement sorting for experiment table view [SCI-7451] [SCI-7497] (#4659) * Implement experiment table sort flyout [SCI-7451] * Implement archive sort options for experiment table [SCI-7497] * Fix hound [SCI-7451] * Clean code for experiment table view sorting [SCI-7497] * Fix hound [SCI-7451] --- app/assets/javascripts/experiments/table.js | 28 ++++++-- app/assets/stylesheets/experiment/table.scss | 16 +++++ .../stylesheets/shared/content_pane.scss | 4 -- app/controllers/experiments_controller.rb | 6 +- app/models/experiment.rb | 18 +++++ .../experiments/table_view_service.rb | 67 ++++++++++++++----- app/views/experiments/_show_header.html.erb | 19 ++++++ config/locales/en.yml | 2 + 8 files changed, 134 insertions(+), 26 deletions(-) diff --git a/app/assets/javascripts/experiments/table.js b/app/assets/javascripts/experiments/table.js index 74243f882..758ef621d 100644 --- a/app/assets/javascripts/experiments/table.js +++ b/app/assets/javascripts/experiments/table.js @@ -8,6 +8,7 @@ var ExperimnetTable = { selectedMyModules: [], activeFilters: {}, filters: [], // Filter {name: '', init(), closeFilter(), apply(), active(), clearFilter()} + myModulesCurrentSort: '', pageSize: GLOBAL_CONSTANTS.DEFAULT_ELEMENTS_PER_PAGE, getUrls: function(id) { return $(`.table-row[data-id="${id}"]`).data('urls'); @@ -20,7 +21,7 @@ var ExperimnetTable = { $(placeholder).insertAfter($(this.table).find('.table-body')); }, appendRows: function(result) { - $.each(result, (id, data) => { + $.each(result, (_j, data) => { let row; // Checkbox selector @@ -28,7 +29,7 @@ var ExperimnetTable = {
- +
`; @@ -59,7 +60,7 @@ var ExperimnetTable = { `; let tableRowClass = `table-row ${data.provisioning_status === 'in_progress' ? 'table-row-provisioning' : ''}`; - $(`
${row}
`) + $(`
${row}
`) .appendTo(`${this.table} .table-body`); }); }, @@ -430,6 +431,18 @@ var ExperimnetTable = { table.loadTable(); }); }, + initSorting: function(table) { + $('#sortMenuDropdown a').click(function() { + if (table.myModulesCurrentSort !== $(this).data('sort')) { + $('#sortMenuDropdown a').removeClass('selected'); + // eslint-disable-next-line no-param-reassign + table.myModulesCurrentSort = $(this).data('sort'); + table.loadTable(); + $(this).addClass('selected'); + $('#sortMenu').dropdown('toggle'); + } + }); + }, initFilters: function() { this.filterDropdown = filterDropdown.init(); let $experimentFilter = $('#experimentTable .my-modules-filters'); @@ -466,9 +479,13 @@ var ExperimnetTable = { }); }, loadTable: function() { + var tableParams = { + filters: this.activeFilters, + sort: this.myModulesCurrentSort + }; var dataUrl = $(this.table).data('my-modules-url'); this.loadPlaceholder(); - $.get(dataUrl, { filters: this.activeFilters }, (result) => { + $.get(dataUrl, tableParams, (result) => { $(this.table).find('.table-row').remove(); this.appendRows(result.data); this.initDueDatePicker(result.data); @@ -484,7 +501,7 @@ var ExperimnetTable = { this.initDueDatePicker(response.data); }, customParams: (params) => { - return { ...params, ...{ filters: this.activeFilters } }; + return { ...params, ...tableParams }; } }); @@ -529,6 +546,7 @@ var ExperimnetTable = { this.initSelector(); this.initSelectAllCheckbox(); this.initFilters(); + this.initSorting(this); this.loadTable(); this.initRenameModal(); this.initAccessModal(); diff --git a/app/assets/stylesheets/experiment/table.scss b/app/assets/stylesheets/experiment/table.scss index a1f8ea912..555049c90 100644 --- a/app/assets/stylesheets/experiment/table.scss +++ b/app/assets/stylesheets/experiment/table.scss @@ -5,6 +5,22 @@ --toolbar-height: 4.5em; position: relative; + .title-row { + .header-actions { + &.experiment-header { + column-gap: .25em; + } + + .sort-task-menu { + &:not(.archived) { + [data-view-mode="archived"] { + display: none; + } + } + } + } + } + .experiment-table-container { height: calc(100vh - var(--content-header-size) - var(--navbar-height) - var(--toolbar-height)); overflow: auto; diff --git a/app/assets/stylesheets/shared/content_pane.scss b/app/assets/stylesheets/shared/content_pane.scss index 1c1e4e3b5..9cee83fd3 100644 --- a/app/assets/stylesheets/shared/content_pane.scss +++ b/app/assets/stylesheets/shared/content_pane.scss @@ -49,10 +49,6 @@ display: flex; flex-shrink: 0; margin-left: auto; - - &.experiment-header { - column-gap: .25em; - } } .view-switch { diff --git a/app/controllers/experiments_controller.rb b/app/controllers/experiments_controller.rb index 45fc7f78a..6f68f4f8c 100644 --- a/app/controllers/experiments_controller.rb +++ b/app/controllers/experiments_controller.rb @@ -90,6 +90,10 @@ class ExperimentsController < ApplicationController def table redirect_to module_archive_experiment_path(@experiment) if @experiment.archived_branch? + view_state = @experiment.current_view_state(current_user) + view_mode = params[:view_mode] || 'active' + @current_sort = view_state.state.dig('my_modules', view_mode, 'sort') || 'atoz' + @project = @experiment.project @active_modules = @experiment.my_modules.active.order(:name) end @@ -97,7 +101,7 @@ class ExperimentsController < ApplicationController def load_table my_modules = @experiment.my_modules.readable_by_user(current_user) my_modules = params[:view_mode] == 'archived' ? my_modules.archived : my_modules.active - render json: Experiments::TableViewService.new(my_modules, current_user, params).call + render json: Experiments::TableViewService.new(@experiment, my_modules, current_user, params).call end def edit diff --git a/app/models/experiment.rb b/app/models/experiment.rb index d8aea4823..290e8744f 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -9,6 +9,7 @@ class Experiment < ApplicationRecord include ArchivableModel include SearchableModel include SearchableByNameModel + include ViewableModel include PermissionCheckableModel include Assignable include Cloneable @@ -94,6 +95,23 @@ class Experiment < ApplicationRecord joins(:project).where(project: { team: teams }) end + def default_view_state + { + my_modules: { + active: { sort: 'atoz' }, + archived: { sort: 'atoz' } + } + } + end + + def validate_view_state(view_state) + if %w(atoz ztoa due_first due_last).exclude?(view_state.state.dig('my_modules', 'active', 'sort')) || + %w(atoz ztoa due_first due_last + archived_old archived_new).exclude?(view_state.state.dig('my_modules', 'archived', 'sort')) + view_state.errors.add(:state, :wrong_state) + end + end + def connections Connection.joins( 'LEFT JOIN my_modules AS inputs ON input_id = inputs.id' diff --git a/app/services/experiments/table_view_service.rb b/app/services/experiments/table_view_service.rb index 0db15bab0..ef23a8f28 100644 --- a/app/services/experiments/table_view_service.rb +++ b/app/services/experiments/table_view_service.rb @@ -35,20 +35,24 @@ module Experiments experiment: :project } - def initialize(my_modules, user, params) + def initialize(experiment, my_modules, user, params) @my_modules = my_modules @page = params[:page] || 1 @user = user @filters = params[:filters] || [] + @params = params + initialize_table_sorting(experiment) end def call - result = {} + result = [] my_module_list = @my_modules @filters.each do |name, value| my_module_list = __send__("#{name}_filter", my_module_list, value) if value.present? end + my_module_list = sort_records(my_module_list) + my_module_list = my_module_list.includes(PRELOAD) .select('my_modules.*') .group('my_modules.id') @@ -67,20 +71,20 @@ module Experiments experiment = my_module.experiment project = experiment.project - result[my_module.id] = { - columns: prepared_my_module, - provisioning_status: my_module.provisioning_status, - urls: { - permissions: permissions_my_module_path(my_module), - actions_dropdown: actions_dropdown_my_module_path(my_module), - name_update: my_module_path(my_module), - restore: restore_my_modules_experiment_path(experiment), - provisioning_status: - my_module.provisioning_status == 'in_progress' && provisioning_status_my_module_url(my_module), - access: edit_access_permissions_project_experiment_my_module_path(project, experiment, my_module) - - } - } + result.push({ id: my_module.id, + columns: prepared_my_module, + provisioning_status: my_module.provisioning_status, + urls: { + permissions: permissions_my_module_path(my_module), + actions_dropdown: actions_dropdown_my_module_path(my_module), + name_update: my_module_path(my_module), + restore: restore_my_modules_experiment_path(experiment), + provisioning_status: + my_module.provisioning_status == 'in_progress' && + provisioning_status_my_module_url(my_module), + access: edit_access_permissions_project_experiment_my_module_path(project, + experiment, my_module) + } }) end { @@ -193,5 +197,36 @@ module Experiments def statuses_filter(my_modules, value) my_modules.where(my_module_status_id: value) end + + def initialize_table_sorting(experiment) + @view_state = experiment.current_view_state(@user) + @view_mode = @params[:view_mode] || 'active' + @sort = @view_state.state.dig('my_modules', @view_mode, 'sort') || 'atoz' + if @params[:sort] && @sort != @params[:sort] && %w(due_first due_last atoz ztoa + archived_old archived_new).include?(@params[:sort]) + @view_state.state['my_modules'].merge!(Hash[@view_mode, { 'sort': @params[:sort] }.stringify_keys]) + @view_state.save! + @sort = @view_state.state.dig('my_modules', @view_mode, 'sort') + end + end + + def sort_records(records) + case @sort + when 'due_first' + records.order(:due_date) + when 'due_last' + records.order(Arel.sql("COALESCE(due_date, DATE '1900-01-01') DESC")) + when 'atoz' + records.order(:name) + when 'ztoa' + records.order(name: :desc) + when 'archived_old' + records.order(Arel.sql('COALESCE(my_modules.archived_on, my_modules.archived_on) ASC')) + when 'archived_new' + records.order(Arel.sql('COALESCE(my_modules.archived_on, my_modules.archived_on) DESC')) + else + records + end + end end end diff --git a/app/views/experiments/_show_header.html.erb b/app/views/experiments/_show_header.html.erb index 2d77efb0c..3e52a2c17 100644 --- a/app/views/experiments/_show_header.html.erb +++ b/app/views/experiments/_show_header.html.erb @@ -63,6 +63,25 @@ <% end %> <% if action_name == 'table' %> <%= render partial: 'table_filters.html.erb' %> + + <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 93a982006..fa6290568 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3224,6 +3224,8 @@ en: ztoa_html: "  Name Z to A" archived_new_html: "Archived last" archived_old_html: "Archived first" + due_first_html: "  Due first" + due_last_html: "  Due last" sort_new: new: "Newest" old: "Oldest"