mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-06 11:57:16 +08:00
Implement bottom action toolbar for repository items [SCI-8300]
Implement bottom action toolbar for repository items [SCI-8300]
This commit is contained in:
parent
ea0d4cf210
commit
aeb84e18d0
10 changed files with 236 additions and 59 deletions
|
@ -52,6 +52,10 @@ var RepositoryDatatable = (function(global) {
|
|||
|
||||
// Enable/disable edit button
|
||||
function updateButtons() {
|
||||
if (window.actionToolbarComponent) {
|
||||
window.actionToolbarComponent.fetchActions({ repository_row_ids: rowsSelected });
|
||||
}
|
||||
|
||||
if (currentMode === 'viewMode') {
|
||||
$(TABLE_WRAPPER_ID).removeClass('editing');
|
||||
$('.repository-save-changes-link').off('click');
|
||||
|
@ -73,22 +77,9 @@ var RepositoryDatatable = (function(global) {
|
|||
}
|
||||
$('#hideRepositoryReminders').show();
|
||||
$('#importRecordsButton').show();
|
||||
if (rowsSelected.length === 0) {
|
||||
$('#exportRepositoriesButton').addClass('disabled');
|
||||
$('#copyRepositoryRecords').prop('disabled', true);
|
||||
$('#editRepositoryRecord').prop('disabled', true);
|
||||
$('#archiveRepositoryRecordsButton').prop('disabled', true);
|
||||
$('#restoreRepositoryRecords').prop('disabled', true);
|
||||
$('#deleteRepositoryRecords').prop('disabled', true);
|
||||
$('#editDeleteCopy').hide();
|
||||
$('#toolbarPrintLabel').hide();
|
||||
} else {
|
||||
|
||||
if (rowsSelected.length !== 0) {
|
||||
$('#editRepositoryRecord').prop('disabled', !allSelectedRowsAreOnPage());
|
||||
$('#exportRepositoriesButton').removeClass('disabled');
|
||||
$('#archiveRepositoryRecordsButton').prop('disabled', false);
|
||||
$('#copyRepositoryRecords').prop('disabled', false);
|
||||
$('#restoreRepositoryRecords').prop('disabled', false);
|
||||
$('#deleteRepositoryRecords').prop('disabled', false);
|
||||
$('#importRecordsButton').hide();
|
||||
|
||||
if (rowsSelected.some(r=> rowsLocked.indexOf(r) >= 0)) { // Some selected rows is rowsLocked
|
||||
|
@ -119,15 +110,12 @@ var RepositoryDatatable = (function(global) {
|
|||
$('#repository-acitons-dropdown').prop('disabled', true);
|
||||
$('.dataTables_length select').prop('disabled', true);
|
||||
$('#addRepositoryRecord').prop('disabled', true);
|
||||
$('#editRepositoryRecord').prop('disabled', true);
|
||||
$('#archiveRepositoryRecordsButton').prop('disabled', true);
|
||||
$('#assignRepositoryRecords').prop('disabled', true);
|
||||
$('#unassignRepositoryRecords').prop('disabled', true);
|
||||
$('#repository-columns-dropdown').find('.dropdown-toggle').prop('disabled', true);
|
||||
$('th').addClass('disable-click');
|
||||
$('.repository-row-selector').prop('disabled', true);
|
||||
$('.dataTables_filter input').prop('disabled', true);
|
||||
$('#toolbarPrintLabel').hide();
|
||||
$('.repository-edit-overlay').show();
|
||||
$('#team-switch').css({ 'pointer-events': 'none', opacity: 0.6 });
|
||||
$('#navigationGoBtn').prop('disabled', true);
|
||||
|
@ -398,11 +386,13 @@ var RepositoryDatatable = (function(global) {
|
|||
}
|
||||
|
||||
function initExportActions() {
|
||||
$('#exportRepositoriesButton').on('click', function() {
|
||||
$(document).on('click', '#exportRepositoriesButton', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
$('#exportRepositoryModal').modal('show');
|
||||
});
|
||||
|
||||
|
||||
$('form#form-export').off().submit(function() {
|
||||
var form = this;
|
||||
if (currentMode === 'viewMode') {
|
||||
|
@ -661,6 +651,8 @@ var RepositoryDatatable = (function(global) {
|
|||
});
|
||||
},
|
||||
fnInitComplete: function() {
|
||||
window.initActionToolbar();
|
||||
|
||||
initHeaderTooltip();
|
||||
disableCheckboxToggleOnCheckboxPreview();
|
||||
|
||||
|
@ -777,7 +769,10 @@ var RepositoryDatatable = (function(global) {
|
|||
changeToEditMode();
|
||||
$('.tooltip').remove();
|
||||
})
|
||||
.on('click', '#copyRepositoryRecords', function() {
|
||||
.on('click', '#copyRepositoryRecords', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('table' + TABLE_ID).data('copy-records'),
|
||||
|
@ -799,7 +794,10 @@ var RepositoryDatatable = (function(global) {
|
|||
}
|
||||
});
|
||||
})
|
||||
.on('click', '#archiveRepositoryRecordsButton', function() {
|
||||
.on('click', '#archiveRepositoryRecordsButton', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('table' + TABLE_ID).data('archive-records'),
|
||||
|
@ -825,7 +823,10 @@ var RepositoryDatatable = (function(global) {
|
|||
}
|
||||
});
|
||||
})
|
||||
.on('click', '#restoreRepositoryRecords', function() {
|
||||
.on('click', '#restoreRepositoryRecords', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
animateSpinner();
|
||||
$.ajax({
|
||||
url: $('table' + TABLE_ID).data('restore-records'),
|
||||
|
@ -851,7 +852,10 @@ var RepositoryDatatable = (function(global) {
|
|||
}
|
||||
});
|
||||
})
|
||||
.on('click', '#editRepositoryRecord', function() {
|
||||
.on('click', '#editRepositoryRecord', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
checkAvailableColumns();
|
||||
|
||||
$(TABLE_ID).find('.repository-row-edit-icon').remove();
|
||||
|
@ -863,14 +867,20 @@ var RepositoryDatatable = (function(global) {
|
|||
changeToEditMode();
|
||||
// adjustTableHeader();
|
||||
})
|
||||
.on('click', '#deleteRepositoryRecords', function() {
|
||||
.on('click', '#deleteRepositoryRecords', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
$('#deleteRepositoryRecord').modal('show');
|
||||
})
|
||||
.on('click', '#hideRepositoryReminders', function() {
|
||||
.on('click', '#hideRepositoryReminders', function(e) {
|
||||
var visibleReminderRepositoryRowIds = $('.row-reminders-dropdown').map(
|
||||
function() { return $(this).closest('[role=row]').attr('id'); }
|
||||
).toArray();
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $(this).data('hideRemindersUrl'),
|
||||
|
|
|
@ -57,11 +57,15 @@
|
|||
return false;
|
||||
});
|
||||
|
||||
$(document).on('click', '.print-label-button', function() {
|
||||
$(document).on('click', '.print-label-button', function(e) {
|
||||
var selectedRows = $(this).data('rows');
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (typeof PrintModalComponent !== 'undefined') {
|
||||
PrintModalComponent.showModal = true;
|
||||
if (selectedRows.length) {
|
||||
if (selectedRows && selectedRows.length) {
|
||||
$('#modal-info-repository-row').modal('hide');
|
||||
PrintModalComponent.row_ids = selectedRows;
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.sn-action-toolbar {
|
||||
background: $color-concrete;
|
||||
z-index: 2;
|
||||
z-index: 100;
|
||||
|
||||
.btn.btn-light:hover {
|
||||
background: $color-white;
|
||||
|
|
|
@ -5,13 +5,15 @@ class RepositoryRowsController < ApplicationController
|
|||
include MyModulesHelper
|
||||
|
||||
MAX_PRINTABLE_ITEM_NAME_LENGTH = 64
|
||||
before_action :load_repository, except: %i(show print rows_to_print print_zpl validate_label_template_columns)
|
||||
before_action :load_repository, except: %i(show print rows_to_print print_zpl
|
||||
validate_label_template_columns actions_toolbar)
|
||||
before_action :load_repository_row_print, only: %i(print rows_to_print print_zpl validate_label_template_columns)
|
||||
before_action :load_repository_or_snapshot, only: %i(print rows_to_print print_zpl validate_label_template_columns)
|
||||
before_action :load_repository_row, only: %i(update assigned_task_list active_reminder_repository_cells)
|
||||
before_action :check_read_permissions, except: %i(show create update delete_records
|
||||
copy_records reminder_repository_cells
|
||||
delete_records archive_records restore_records)
|
||||
delete_records archive_records restore_records
|
||||
actions_toolbar)
|
||||
before_action :check_snapshotting_status, only: %i(create update delete_records copy_records)
|
||||
before_action :check_create_permissions, only: :create
|
||||
before_action :check_delete_permissions, only: %i(delete_records archive_records restore_records)
|
||||
|
@ -288,6 +290,16 @@ class RepositoryRowsController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def actions_toolbar
|
||||
render json: {
|
||||
actions:
|
||||
Toolbars::RepositoryRowsService.new(
|
||||
current_user,
|
||||
repository_row_ids: params[:repository_row_ids].split(',')
|
||||
).actions
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
include StringUtility
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div v-if="actions.length" class="sn-action-toolbar p-4 w-full fixed bottom-0 rounded-t-md shadow-[0_-12px_24px_-12px_rgba(35,31,32,0.2)]" :style="`width: ${width}px`">
|
||||
<div v-if="loading || actions.length" class="sn-action-toolbar p-4 w-full fixed bottom-0 rounded-t-md shadow-[0_-12px_24px_-12px_rgba(35,31,32,0.2)]" :style="`width: ${width}px`">
|
||||
<div class="sn-action-toolbar__actions flex">
|
||||
<div v-if="loading && !actions.length" class="sn-action-toolbar__action">
|
||||
<a class="btn btn-light"></a>
|
||||
</div>
|
||||
<div v-for="action in actions" :key="action.name" class="sn-action-toolbar__action">
|
||||
<a :class="`btn btn-light ${action.button_class}`"
|
||||
:href="(['link', 'remote-modal']).includes(action.type) ? action.path : '#'"
|
||||
|
@ -19,6 +22,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {debounce} from '../shared/debounce.js';
|
||||
|
||||
export default {
|
||||
name: 'ActionToolbar',
|
||||
props: {
|
||||
|
@ -31,12 +36,22 @@
|
|||
multiple: false,
|
||||
params: {},
|
||||
reloadCallback: null,
|
||||
loading: false,
|
||||
width: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.actionToolbarComponent = this;
|
||||
window.onresize = this.setWidth;
|
||||
|
||||
this.debouncedFetchActions = debounce((params) => {
|
||||
this.params = params;
|
||||
|
||||
$.get(`${this.actionsUrl}?${new URLSearchParams(this.params).toString()}`, (data) => {
|
||||
this.actions = data.actions;
|
||||
this.loading = false;
|
||||
});
|
||||
}, 200);
|
||||
},
|
||||
mounted() {
|
||||
this.setWidth();
|
||||
|
@ -49,11 +64,8 @@
|
|||
this.width = $(this.$el).parent().width();
|
||||
},
|
||||
fetchActions(params) {
|
||||
this.params = params;
|
||||
|
||||
$.get(`${this.actionsUrl}?${new URLSearchParams(this.params).toString()}`, (data) => {
|
||||
this.actions = data.actions;
|
||||
});
|
||||
this.loading = true;
|
||||
this.debouncedFetchActions(params);
|
||||
},
|
||||
setReloadCallback(func) {
|
||||
this.reloadCallback = func;
|
||||
|
|
140
app/services/toolbars/repository_rows_service.rb
Normal file
140
app/services/toolbars/repository_rows_service.rb
Normal file
|
@ -0,0 +1,140 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositoryMismatchError < StandardError; end
|
||||
|
||||
module Toolbars
|
||||
class RepositoryRowsService
|
||||
attr_reader :current_user
|
||||
|
||||
include Canaid::Helpers::PermissionsHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
def initialize(current_user, repository_row_ids: [])
|
||||
@current_user = current_user
|
||||
@repository_rows = RepositoryRow.where(id: repository_row_ids)
|
||||
|
||||
return if @repository_rows.none?
|
||||
|
||||
if @repository_rows.pluck(:repository_id).uniq.size != 1
|
||||
raise RepositoryMismatchError, 'Items are not from the same repository!'
|
||||
end
|
||||
|
||||
@repository = @repository_rows.first.repository
|
||||
|
||||
@single = @repository_rows.length == 1
|
||||
end
|
||||
|
||||
def actions
|
||||
return [] if @repository_rows.none?
|
||||
|
||||
[
|
||||
restore_action,
|
||||
edit_action,
|
||||
duplicate_action,
|
||||
export_action,
|
||||
print_label_action,
|
||||
archive_action,
|
||||
delete_action
|
||||
].compact
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def restore_action
|
||||
return unless can_manage_repository_rows?(@repository)
|
||||
|
||||
return unless @repository_rows.all?(&:archived?)
|
||||
|
||||
{
|
||||
name: 'restore',
|
||||
label: I18n.t('repositories.restore_record'),
|
||||
icon: 'fas fa-undo',
|
||||
button_class: 'resotre-repository-row-btn',
|
||||
button_id: 'restoreRepositoryRecords',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
|
||||
def edit_action
|
||||
return unless can_manage_repository_rows?(@repository)
|
||||
|
||||
{
|
||||
name: 'edit',
|
||||
label: I18n.t('repositories.edit_record'),
|
||||
icon: 'fas fa-pencil-alt',
|
||||
button_class: 'edit-repository-row-btn',
|
||||
button_id: 'editRepositoryRecord',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
|
||||
def duplicate_action
|
||||
return unless can_create_repository_rows?(@repository)
|
||||
|
||||
{
|
||||
name: 'duplicate',
|
||||
label: I18n.t('repositories.copy_record'),
|
||||
icon: 'fas fa-copy',
|
||||
button_class: 'copy-repository-row-btn',
|
||||
button_id: 'copyRepositoryRecords',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
|
||||
def export_action
|
||||
return unless can_read_repository?(@repository)
|
||||
|
||||
{
|
||||
name: 'export',
|
||||
label: I18n.t('repositories.export_record'),
|
||||
icon: 'fas fa-file-export',
|
||||
button_class: 'export-repository-row-btn',
|
||||
button_id: 'exportRepositoriesButton',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
|
||||
def print_label_action
|
||||
return unless can_read_repository?(@repository)
|
||||
|
||||
{
|
||||
name: 'print_label',
|
||||
label: I18n.t('repositories.print_label'),
|
||||
icon: 'fas fa-print',
|
||||
button_class: 'print-label-button',
|
||||
button_id: 'toolbarPrintLabel',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
|
||||
def archive_action
|
||||
return unless can_manage_repository_rows?(@repository)
|
||||
|
||||
return unless @repository_rows.all?(&:active?)
|
||||
|
||||
{
|
||||
name: 'archive',
|
||||
label: I18n.t('repositories.archive_record'),
|
||||
icon: 'fas fa-archive',
|
||||
button_class: 'resotre-repository-row-btn',
|
||||
button_id: 'archiveRepositoryRecordsButton',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
|
||||
def delete_action
|
||||
return unless can_delete_repository_rows?(@repository)
|
||||
|
||||
return unless @repository_rows.all?(&:archived?)
|
||||
|
||||
{
|
||||
name: 'delete',
|
||||
label: I18n.t('repositories.delete_record'),
|
||||
icon: 'fas fa-trash',
|
||||
button_class: 'resotre-repository-row-btn',
|
||||
button_id: 'deleteRepositoryRecords',
|
||||
type: :legacy
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,30 +25,18 @@
|
|||
<span class="button-text"><%= t('repositories.index.options_dropdown.import_items') %></span>
|
||||
</button>
|
||||
<% end %>
|
||||
<!--
|
||||
<%= render partial: 'repositories/toolbar/row_actions' %>
|
||||
<% if @repository.repository_rows.with_active_reminders(current_user).any? %>
|
||||
<button type="button" data-toggle="tooltip" data-placement="bottom" title="<%= t("repositories.hide_reminders") %>"
|
||||
class="btn btn-light auto-shrink-button"
|
||||
id="hideRepositoryReminders"
|
||||
data-view-mode="active"
|
||||
data-hide-reminders-url="<%= team_repository_hide_reminders_url(@current_team, @repository) %>">
|
||||
<span class="fas fa-bell-slash"></span>
|
||||
<span class="button-text"><%= t("repositories.hide_reminders") %></span>
|
||||
|
||||
<span id="saveCancel" data-toggle="buttons" style="display:none">
|
||||
<button type="button" class="btn btn-success prevent-shrink" id="saveRecord" data-view-mode="active">
|
||||
<span class="fas fa-save"></span>
|
||||
<%= t("repositories.save_record") %>
|
||||
</button>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render partial: 'repositories/toolbar/print_label_button' %>
|
||||
<button type="button" class="btn btn-light prevent-shrink" id="cancelSave" data-view-mode="active">
|
||||
<span class="fas fa-times-circle"></span>
|
||||
<%= t("repositories.cancel_save") %>
|
||||
</button>
|
||||
</span>
|
||||
<% end %>
|
||||
<div class="archived-label" data-view-mode="archived">
|
||||
<%= render partial: 'repositories/toolbar/archive_label' %>
|
||||
</div>
|
||||
<% if can_manage_repository_filters?(@repository) %>
|
||||
<div class="toolbar-save-filters">
|
||||
<%= render partial: 'repositories/toolbar/save_filters' %>
|
||||
</div>
|
||||
<% end %>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div class="toolbar-middle-block">
|
||||
|
|
|
@ -67,6 +67,10 @@
|
|||
repository_index_link: repository_table_index_path(@repository)
|
||||
}
|
||||
%>
|
||||
|
||||
<div id="actionToolbar" data-behaviour="vue">
|
||||
<action-toolbar actions-url="<%= actions_toolbar_repository_rows_url %>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'repositories/import_repository_records_modal',
|
||||
|
@ -89,6 +93,7 @@
|
|||
<%= render partial: 'save_repository_filter_modal' %>
|
||||
<% end %>
|
||||
|
||||
<%= javascript_include_tag 'vue_components_action_toolbar' %>
|
||||
<%= javascript_include_tag 'vue_repository_search' %>
|
||||
<%= javascript_include_tag 'repositories/edit', nonce: true %>
|
||||
<%= javascript_include_tag 'repositories/repository_datatable', nonce: true %>
|
||||
|
|
|
@ -1960,6 +1960,8 @@ en:
|
|||
delete_record: "Delete"
|
||||
archive_record: "Archive"
|
||||
restore_record: "Restore"
|
||||
print_label: "Print label"
|
||||
export_record: "Export"
|
||||
save_record: "Save"
|
||||
cancel_save: "Cancel"
|
||||
hide_reminders: "Clear all reminders"
|
||||
|
|
|
@ -664,6 +664,10 @@ Rails.application.routes.draw do
|
|||
get :print_zpl
|
||||
post :validate_label_template_columns
|
||||
end
|
||||
|
||||
collection do
|
||||
get :actions_toolbar
|
||||
end
|
||||
end
|
||||
|
||||
resources :repositories do
|
||||
|
|
Loading…
Add table
Reference in a new issue