Merge pull request #4688 from aignatov-bio/ai-sci-7521-add-new-actions-toolbar-to-datatable

Add actions toolbar to protocol repository [SCI-7521]
This commit is contained in:
aignatov-bio 2022-12-07 14:49:45 +01:00 committed by GitHub
commit a901b3db59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 175 additions and 18 deletions

View file

@ -0,0 +1,5 @@
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5 2H3.5C3.22386 2 3 2.22386 3 2.5C3 2.77614 3.22386 3 3.5 3H12.5C12.7761 3 13 2.77614 13 2.5C13 2.22386 12.7761 2 12.5 2Z" fill="white"/>
<path d="M10.5 0H5.5C5.22386 0 5 0.223858 5 0.5C5 0.776142 5.22386 1 5.5 1H10.5C10.7761 1 11 0.776142 11 0.5C11 0.223858 10.7761 0 10.5 0Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.61485 14C2.25734 14 1.94953 13.7477 1.87942 13.3971L0.119612 4.59806C0.0577324 4.28866 0.294379 4 0.609902 4H15.3901C15.7056 4 15.9423 4.28866 15.8804 4.59806L14.1206 13.3971C14.0505 13.7477 13.7427 14 13.3851 14H2.61485ZM10.5 8H5.5C5.22386 8 5 8.22386 5 8.5C5 8.77614 5.22386 9 5.5 9H10.5C10.7761 9 11 8.77614 11 8.5C11 8.22386 10.7761 8 10.5 8Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 824 B

View file

@ -1,7 +1,9 @@
//= require protocols/import_export/import
/* eslint-disable no-use-before-define */
/* global ProtocolRepositoryHeader PdfPreview DataTableHelpers */
// Global variables
var PERMISSIONS = ['archivable', 'restorable', 'copyable'];
var rowsSelected = [];
var protocolsTableEl = null;
var protocolsDatatable = null;
@ -27,7 +29,7 @@ function initProtocolsTable() {
protocolsDatatable = protocolsTableEl.DataTable({
order: [[1, "asc"]],
dom: "R<'main-actions hidden'<'toolbar'><'filter-container'f>>t<'pagination-row hidden'<'pagination-info'li><'pagination-actions'p>>",
dom: "R<'main-actions hidden'<'toolbar'><'filter-container'f>>t<'pagination-row hidden'<'actions-toolbar'><'pagination-info'li><'pagination-actions'p>>",
stateSave: true,
sScrollX: '100%',
sScrollXInner: '100%',
@ -115,6 +117,9 @@ function initProtocolsTable() {
DataTableHelpers.initLengthAppearance(dataTableWrapper);
DataTableHelpers.initSearchField(dataTableWrapper, 'Enter...');
dataTableWrapper.find('.main-actions, .pagination-row').removeClass('hidden');
let actionToolBar = $($('#protocolActionToolbar').html());
$('.protocols-container .actions-toolbar').html(actionToolBar);
},
stateLoadCallback: function (settings) {
// Load the table state for the current team
@ -137,6 +142,26 @@ function initProtocolsTable() {
});
}
function checkActionPermission(permission) {
let allProtocols;
allProtocols = rowsSelected.every((id) => {
return protocolsTableEl.find(`tbody tr#${id}`).data(permission);
});
return allProtocols;
}
function loadPermission(id) {
let row = protocolsTableEl.find(`tbody tr#${id}`);
$.get(row.data('permissions-url'), (result) => {
PERMISSIONS.forEach((permission) => {
row.data(permission, result[permission]);
});
updateButtons();
});
}
function initRowSelection() {
let protocolsTableScrollHead = protocolsTableEl.closest('.dataTables_scroll').find('.dataTables_scrollHead');
@ -163,15 +188,16 @@ function initRowSelection() {
rowsSelected.splice(index, 1);
}
updateDataTableSelectAllCheckbox();
if (this.checked) {
row.addClass("selected");
loadPermission(rowId);
row.addClass('selected');
} else {
row.removeClass("selected");
row.removeClass('selected');
updateButtons();
}
updateDataTableSelectAllCheckbox();
e.stopPropagation();
updateButtons();
});
// Handle click on "Select all" control
@ -390,6 +416,34 @@ function updateDataTableSelectAllCheckbox() {
}
}
function updateButtons() {
let actionToolbar = $('.protocols-container .actions-toolbar');
$('.dataTables_wrapper').addClass('show-actions');
if (rowsSelected.length === 0) {
$('.dataTables_wrapper').removeClass('show-actions');
} else if (rowsSelected.length === 1) {
actionToolbar.find('.single-object-action, .multiple-object-action').removeClass('hidden');
} else {
actionToolbar.find('.single-object-action').addClass('hidden');
actionToolbar.find('.multiple-object-action').removeClass('hidden');
}
PERMISSIONS.forEach((permission) => {
if (!checkActionPermission(permission)) {
actionToolbar.find(`.btn[data-for="${permission}"]`).addClass('hidden');
}
});
if (protocolsDatatable) protocolsDatatable.columns.adjust();
actionToolbar.find('.btn').addClass('notransition');
actionToolbar.find('.btn').removeClass('btn-primary').addClass('btn-light');
actionToolbar.find('.btn:visible').first().addClass('btn-primary').removeClass('btn-light');
actionToolbar.find('.btn').removeClass('notransition');
}
/*
function updateButtons() {
var cloneBtn = $("[data-action='clone']");
var makePrivateBtn = $("[data-action='make-private']");
@ -506,6 +560,7 @@ function updateButtons() {
}
}
}
*/
function exportProtocols(ids) {
if (ids.length > 0) {

View file

@ -3,6 +3,7 @@
.protocols-index {
.protocols-datatable {
--content-header-size: 5em;
--protocol-toolbar-size: 4em;
height: calc(100vh - var(--navbar-height) - var(--content-header-size));
#protocols-table_wrapper {
@ -16,7 +17,7 @@
display: flex;
flex-direction: column;
flex-grow: 1;
height: calc(100% - var(--datatable-pagination-row));
height: calc(100% - var(--datatable-pagination-row) - var(--protocol-toolbar-size));
}
.dataTables_scrollBody {

View file

@ -41,8 +41,26 @@
display: flex;
flex-wrap: wrap;
min-height: var(--datatable-pagination-row);
position: relative;
width: 100%;
.actions-toolbar {
align-items: center;
background-color: $color-concrete;
border-bottom: 1px solid $color-alto;
display: none;
height: 70px;
overflow-x: auto;
padding: 0 1em;
position: absolute;
top: 0;
width: 100%;
.btn {
margin-right: .25em;
}
}
.pagination-info,
.pagination-actions {
flex-grow: 1;
@ -107,4 +125,31 @@
table > tbody > tr:first-child > td {
border-top: 0;
}
&.show-actions {
--datatable-pagination-row: 139px;
.pagination-row {
padding-top: 71px;
.actions-toolbar {
display: flex;
}
}
}
@media (max-width: 1000px) {
.pagination-row .actions-toolbar {
.btn {
.button-text {
display: none;
}
.fas,
img {
margin: 0;
}
}
}
}
}

View file

@ -1,3 +1,5 @@
// scss-lint:disable ImportantRule VendorPrefix
$timing-function-deceleration: cubic-bezier(0, 0, .2, 1) !default;
$timing-function-standard: cubic-bezier(.4, 0, .2, 1) !default;
$timing-function-acceleration: cubic-bezier(.4, 0, 1, 1) !default;
@ -13,3 +15,10 @@ $timing-function-sharp: cubic-bezier(.4, 0, .6, 1) !default;
animation: pulse 1s infinite;
animation-timing-function: ease-in-out;
}
.notransition {
-moz-transition: none !important;
-o-transition: none !important;
-webkit-transition: none !important;
transition: none !important;
}

View file

@ -22,6 +22,10 @@
margin-right: .25em;
}
img {
padding-bottom: 3px;
}
&:hover {
text-decoration: none;
}

View file

@ -21,6 +21,7 @@ class ProtocolsController < ApplicationController
preview
linked_children
linked_children_datatable
permissions
)
before_action :switch_team_with_param, only: :index
before_action :check_view_all_permissions, only: %i(
@ -1024,6 +1025,16 @@ class ProtocolsController < ApplicationController
end
end
def permissions
if stale?(@protocol)
render json: {
copyable: can_clone_protocol_in_repository?(@protocol),
archivable: can_manage_protocol_in_repository?(@protocol),
restorable: can_restore_protocol_in_repository?(@protocol)
}
end
end
private
def set_importer

View file

@ -2,6 +2,7 @@ class ProtocolsDatatable < CustomDatatable
# Needed for sanitize_sql_like method
include ActiveRecord::Sanitization::ClassMethods
include InputSanitizeHelper
include Rails.application.routes.url_helpers
def_delegator :@view, :can_read_protocol_in_repository?
def_delegator :@view, :can_manage_protocol_in_repository?
@ -81,17 +82,9 @@ class ProtocolsDatatable < CustomDatatable
protocol = Protocol.find(record.id)
result_data << {
'DT_RowId': record.id,
'DT_CanClone': can_clone_protocol_in_repository?(protocol),
'DT_CloneUrl': if can_clone_protocol_in_repository?(protocol)
clone_protocol_path(protocol,
team: @team,
type: @type)
end,
'DT_CanMakePrivate': can_manage_protocol_in_repository?(protocol),
'DT_CanPublish': can_manage_protocol_in_repository?(protocol),
'DT_CanArchive': can_manage_protocol_in_repository?(protocol),
'DT_CanRestore': can_restore_protocol_in_repository?(protocol),
'DT_CanExport': can_read_protocol_in_repository?(protocol),
'DT_RowAttr': {
'data-permissions-url': permissions_protocol_path(protocol)
},
'1': if protocol.in_repository_archived?
escape_input(record.name)
else

View file

@ -96,7 +96,7 @@
<div id="protocolsio-preview-modal-target"></div>
<%= render partial: "protocols/import_export/import_json_protocol_modal.html.erb" %>
<%= render partial: "protocols/index/action_toolbar.html.erb" %>
<%= render partial: "protocols/index/make_private_results_modal.html.erb" %>
<%= render partial: "protocols/index/publish_results_modal.html.erb" %>
<%= render partial: "protocols/index/confirm_archive_modal.html.erb" %>

View file

@ -0,0 +1,26 @@
<template id="protocolActionToolbar">
<button id="protocolVersions" class="btn btn-light single-object-action hidden only-active">
<%= image_tag 'icon/versions.svg' %>
<span class="button-text"><%= t("protocols.index.action_toolbar.versions") %></span>
</button>
<button id="manageProtocolAccess" class="btn btn-light single-object-action hidden">
<i class="fas fa-door-open"></i>
<span class="button-text"><%= t("protocols.index.action_toolbar.access") %></span>
</button>
<button id="duplicateProtocol" class="btn btn-light multiple-object-action hidden only-active" data-for="copyable">
<i class="fas fa-copy"></i>
<span class="button-text"><%= t("protocols.index.action_toolbar.duplicate") %></span>
</button>
<button id="moveTask" class="btn btn-light multiple-object-action hidden only-active">
<i class="fas fa-download"></i>
<span class="button-text"><%= t("protocols.index.action_toolbar.export") %></span>
</button>
<button id="archiveProtocol" class="btn btn-light multiple-object-action hidden only-active" data-for="archivable">
<i class="fas fa-archive"></i>
<span class="button-text"><%= t("protocols.index.action_toolbar.archive") %></span>
</button>
<button id="restoreProtocol" class="btn btn-light multiple-object-action hidden only-archive" data-for="restorable">
<i class="fas fa-undo"></i>
<span class="button-text"><%= t("protocols.index.action_toolbar.restore") %></span>
</button>
</template>

View file

@ -2555,6 +2555,13 @@ en:
private: "My protocols"
external_protocols: "External protocols"
archive: "Archive"
action_toolbar:
versions: "Versions"
access: "Manage Access"
duplicate: "Duplicate"
export: "Export"
archive: "Archive"
restore: "Restore"
public_description: "Team protocols are visible and can be used by everyone from the team."
private_description: "My protocols are only visible to you."
create_new: "New"

View file

@ -581,6 +581,7 @@ Rails.application.routes.draw do
get 'edit_authors_modal', to: 'protocols#edit_authors_modal'
get 'edit_description_modal', to: 'protocols#edit_description_modal'
post 'delete_steps'
get :permissions
end
collection do
post 'datatable', to: 'protocols#datatable'