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]
This commit is contained in:
ajugo 2022-12-05 10:30:20 +01:00 committed by GitHub
parent fdf13f822e
commit 02f039ffed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 26 deletions

View file

@ -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 = {
<div class="table-body-cell">
<div class="sci-checkbox-container">
<div class="loading-overlay"></div>
<input type="checkbox" class="sci-checkbox my-module-selector" data-my-module="${id}">
<input type="checkbox" class="sci-checkbox my-module-selector" data-my-module="${data.id}">
<span class="sci-checkbox-label"></span>
</div>
</div>`;
@ -59,7 +60,7 @@ var ExperimnetTable = {
</div>`;
let tableRowClass = `table-row ${data.provisioning_status === 'in_progress' ? 'table-row-provisioning' : ''}`;
$(`<div class="${tableRowClass}" data-urls='${JSON.stringify(data.urls)}' data-id="${id}">${row}</div>`)
$(`<div class="${tableRowClass}" data-urls='${JSON.stringify(data.urls)}' data-id="${data.id}">${row}</div>`)
.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();

View file

@ -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;

View file

@ -49,10 +49,6 @@
display: flex;
flex-shrink: 0;
margin-left: auto;
&.experiment-header {
column-gap: .25em;
}
}
.view-switch {

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -63,6 +63,25 @@
<% end %>
<% if action_name == 'table' %>
<%= render partial: 'table_filters.html.erb' %>
<div class="dropdown sort-menu">
<button class="btn btn-light icon-btn dropdown-toggle" type="button" id="sortMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fas fa-sort-amount-up"></i>
</button>
<ul id="sortMenuDropdown" class="dropdown-menu sort-task-menu <%= params[:view_mode] %> dropdown-menu-right" aria-labelledby="sortMenu">
<% %w(atoz ztoa due_first due_last archived_old archived_new).each_with_index do |sort, i| %>
<% if i.even? && i.positive? %>
<li class="divider" <%= i > 3 ? 'data-view-mode=archived' : '' %>></li>
<% end %>
<li <%= %w(archived_new archived_old).include?(sort) ? 'data-view-mode=archived' : '' %>>
<a class="<%= 'selected' if @current_sort == sort %>"
data-sort="<%= sort %>" >
<%= t("general.sort.#{sort}_html") %>
</a>
</li>
<% end %>
</ul>
</div>
<% end %>
</div>
</div>

View file

@ -3224,6 +3224,8 @@ en:
ztoa_html: "<i class=\"fas fa-sort-alpha-up\"></i>&nbsp;&nbsp;Name Z to A"
archived_new_html: "<span class=\"fa-stack\"><i class=\"fas fa-long-arrow-alt-up\"></i><i class=\"fas fa-archive\"></i></span>Archived last"
archived_old_html: "<span class=\"fa-stack\"><i class=\"fas fa-long-arrow-alt-down\"></i><i class=\"fas fa-archive\"></i></span>Archived first"
due_first_html: "<i class=\"fas fa-sort-numeric-up\"></i>&nbsp;&nbsp;Due first"
due_last_html: "<i class=\"fas fa-sort-numeric-up\"></i>&nbsp;&nbsp;Due last"
sort_new:
new: "Newest"
old: "Oldest"