mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-06 11:57:16 +08:00
Add actions toolbar to protocol repository [SCI-7521]
This commit is contained in:
parent
2bb11af170
commit
2a8aaed7d1
12 changed files with 175 additions and 18 deletions
5
app/assets/images/icon/versions.svg
Normal file
5
app/assets/images/icon/versions.svg
Normal 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 |
|
@ -1,7 +1,9 @@
|
||||||
//= require protocols/import_export/import
|
//= require protocols/import_export/import
|
||||||
|
/* eslint-disable no-use-before-define */
|
||||||
/* global ProtocolRepositoryHeader PdfPreview DataTableHelpers */
|
/* global ProtocolRepositoryHeader PdfPreview DataTableHelpers */
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
|
var PERMISSIONS = ['archivable', 'restorable', 'copyable'];
|
||||||
var rowsSelected = [];
|
var rowsSelected = [];
|
||||||
var protocolsTableEl = null;
|
var protocolsTableEl = null;
|
||||||
var protocolsDatatable = null;
|
var protocolsDatatable = null;
|
||||||
|
@ -27,7 +29,7 @@ function initProtocolsTable() {
|
||||||
|
|
||||||
protocolsDatatable = protocolsTableEl.DataTable({
|
protocolsDatatable = protocolsTableEl.DataTable({
|
||||||
order: [[1, "asc"]],
|
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,
|
stateSave: true,
|
||||||
sScrollX: '100%',
|
sScrollX: '100%',
|
||||||
sScrollXInner: '100%',
|
sScrollXInner: '100%',
|
||||||
|
@ -115,6 +117,9 @@ function initProtocolsTable() {
|
||||||
DataTableHelpers.initLengthAppearance(dataTableWrapper);
|
DataTableHelpers.initLengthAppearance(dataTableWrapper);
|
||||||
DataTableHelpers.initSearchField(dataTableWrapper, 'Enter...');
|
DataTableHelpers.initSearchField(dataTableWrapper, 'Enter...');
|
||||||
dataTableWrapper.find('.main-actions, .pagination-row').removeClass('hidden');
|
dataTableWrapper.find('.main-actions, .pagination-row').removeClass('hidden');
|
||||||
|
|
||||||
|
let actionToolBar = $($('#protocolActionToolbar').html());
|
||||||
|
$('.protocols-container .actions-toolbar').html(actionToolBar);
|
||||||
},
|
},
|
||||||
stateLoadCallback: function (settings) {
|
stateLoadCallback: function (settings) {
|
||||||
// Load the table state for the current team
|
// 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() {
|
function initRowSelection() {
|
||||||
let protocolsTableScrollHead = protocolsTableEl.closest('.dataTables_scroll').find('.dataTables_scrollHead');
|
let protocolsTableScrollHead = protocolsTableEl.closest('.dataTables_scroll').find('.dataTables_scrollHead');
|
||||||
|
|
||||||
|
@ -163,15 +188,16 @@ function initRowSelection() {
|
||||||
rowsSelected.splice(index, 1);
|
rowsSelected.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDataTableSelectAllCheckbox();
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
row.addClass("selected");
|
loadPermission(rowId);
|
||||||
|
row.addClass('selected');
|
||||||
} else {
|
} else {
|
||||||
row.removeClass("selected");
|
row.removeClass('selected');
|
||||||
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDataTableSelectAllCheckbox();
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
updateButtons();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle click on "Select all" control
|
// 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() {
|
function updateButtons() {
|
||||||
var cloneBtn = $("[data-action='clone']");
|
var cloneBtn = $("[data-action='clone']");
|
||||||
var makePrivateBtn = $("[data-action='make-private']");
|
var makePrivateBtn = $("[data-action='make-private']");
|
||||||
|
@ -506,6 +560,7 @@ function updateButtons() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
function exportProtocols(ids) {
|
function exportProtocols(ids) {
|
||||||
if (ids.length > 0) {
|
if (ids.length > 0) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
.protocols-index {
|
.protocols-index {
|
||||||
.protocols-datatable {
|
.protocols-datatable {
|
||||||
--content-header-size: 5em;
|
--content-header-size: 5em;
|
||||||
|
--protocol-toolbar-size: 4em;
|
||||||
height: calc(100vh - var(--navbar-height) - var(--content-header-size));
|
height: calc(100vh - var(--navbar-height) - var(--content-header-size));
|
||||||
|
|
||||||
#protocols-table_wrapper {
|
#protocols-table_wrapper {
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
height: calc(100% - var(--datatable-pagination-row));
|
height: calc(100% - var(--datatable-pagination-row) - var(--protocol-toolbar-size));
|
||||||
}
|
}
|
||||||
|
|
||||||
.dataTables_scrollBody {
|
.dataTables_scrollBody {
|
||||||
|
|
|
@ -41,8 +41,26 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
min-height: var(--datatable-pagination-row);
|
min-height: var(--datatable-pagination-row);
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
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-info,
|
||||||
.pagination-actions {
|
.pagination-actions {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -107,4 +125,31 @@
|
||||||
table > tbody > tr:first-child > td {
|
table > tbody > tr:first-child > td {
|
||||||
border-top: 0;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// scss-lint:disable ImportantRule VendorPrefix
|
||||||
|
|
||||||
$timing-function-deceleration: cubic-bezier(0, 0, .2, 1) !default;
|
$timing-function-deceleration: cubic-bezier(0, 0, .2, 1) !default;
|
||||||
$timing-function-standard: cubic-bezier(.4, 0, .2, 1) !default;
|
$timing-function-standard: cubic-bezier(.4, 0, .2, 1) !default;
|
||||||
$timing-function-acceleration: cubic-bezier(.4, 0, 1, 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: pulse 1s infinite;
|
||||||
animation-timing-function: ease-in-out;
|
animation-timing-function: ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notransition {
|
||||||
|
-moz-transition: none !important;
|
||||||
|
-o-transition: none !important;
|
||||||
|
-webkit-transition: none !important;
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
margin-right: .25em;
|
margin-right: .25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ class ProtocolsController < ApplicationController
|
||||||
preview
|
preview
|
||||||
linked_children
|
linked_children
|
||||||
linked_children_datatable
|
linked_children_datatable
|
||||||
|
permissions
|
||||||
)
|
)
|
||||||
before_action :switch_team_with_param, only: :index
|
before_action :switch_team_with_param, only: :index
|
||||||
before_action :check_view_all_permissions, only: %i(
|
before_action :check_view_all_permissions, only: %i(
|
||||||
|
@ -1024,6 +1025,16 @@ class ProtocolsController < ApplicationController
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def set_importer
|
def set_importer
|
||||||
|
|
|
@ -2,6 +2,7 @@ class ProtocolsDatatable < CustomDatatable
|
||||||
# Needed for sanitize_sql_like method
|
# Needed for sanitize_sql_like method
|
||||||
include ActiveRecord::Sanitization::ClassMethods
|
include ActiveRecord::Sanitization::ClassMethods
|
||||||
include InputSanitizeHelper
|
include InputSanitizeHelper
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
def_delegator :@view, :can_read_protocol_in_repository?
|
def_delegator :@view, :can_read_protocol_in_repository?
|
||||||
def_delegator :@view, :can_manage_protocol_in_repository?
|
def_delegator :@view, :can_manage_protocol_in_repository?
|
||||||
|
@ -81,17 +82,9 @@ class ProtocolsDatatable < CustomDatatable
|
||||||
protocol = Protocol.find(record.id)
|
protocol = Protocol.find(record.id)
|
||||||
result_data << {
|
result_data << {
|
||||||
'DT_RowId': record.id,
|
'DT_RowId': record.id,
|
||||||
'DT_CanClone': can_clone_protocol_in_repository?(protocol),
|
'DT_RowAttr': {
|
||||||
'DT_CloneUrl': if can_clone_protocol_in_repository?(protocol)
|
'data-permissions-url': permissions_protocol_path(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),
|
|
||||||
'1': if protocol.in_repository_archived?
|
'1': if protocol.in_repository_archived?
|
||||||
escape_input(record.name)
|
escape_input(record.name)
|
||||||
else
|
else
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
|
|
||||||
<div id="protocolsio-preview-modal-target"></div>
|
<div id="protocolsio-preview-modal-target"></div>
|
||||||
<%= render partial: "protocols/import_export/import_json_protocol_modal.html.erb" %>
|
<%= 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/make_private_results_modal.html.erb" %>
|
||||||
<%= render partial: "protocols/index/publish_results_modal.html.erb" %>
|
<%= render partial: "protocols/index/publish_results_modal.html.erb" %>
|
||||||
<%= render partial: "protocols/index/confirm_archive_modal.html.erb" %>
|
<%= render partial: "protocols/index/confirm_archive_modal.html.erb" %>
|
||||||
|
|
26
app/views/protocols/index/_action_toolbar.html.erb
Normal file
26
app/views/protocols/index/_action_toolbar.html.erb
Normal 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>
|
|
@ -2555,6 +2555,13 @@ en:
|
||||||
private: "My protocols"
|
private: "My protocols"
|
||||||
external_protocols: "External protocols"
|
external_protocols: "External protocols"
|
||||||
archive: "Archive"
|
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."
|
public_description: "Team protocols are visible and can be used by everyone from the team."
|
||||||
private_description: "My protocols are only visible to you."
|
private_description: "My protocols are only visible to you."
|
||||||
create_new: "New"
|
create_new: "New"
|
||||||
|
|
|
@ -581,6 +581,7 @@ Rails.application.routes.draw do
|
||||||
get 'edit_authors_modal', to: 'protocols#edit_authors_modal'
|
get 'edit_authors_modal', to: 'protocols#edit_authors_modal'
|
||||||
get 'edit_description_modal', to: 'protocols#edit_description_modal'
|
get 'edit_description_modal', to: 'protocols#edit_description_modal'
|
||||||
post 'delete_steps'
|
post 'delete_steps'
|
||||||
|
get :permissions
|
||||||
end
|
end
|
||||||
collection do
|
collection do
|
||||||
post 'datatable', to: 'protocols#datatable'
|
post 'datatable', to: 'protocols#datatable'
|
||||||
|
|
Loading…
Add table
Reference in a new issue