Implement bottom action toolbar for repository items [SCI-8300]

Implement bottom action toolbar for repository items [SCI-8300]
This commit is contained in:
artoscinote 2023-05-17 11:27:55 +02:00 committed by GitHub
parent ea0d4cf210
commit aeb84e18d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 236 additions and 59 deletions

View file

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

View file

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

View file

@ -1,6 +1,6 @@
.sn-action-toolbar {
background: $color-concrete;
z-index: 2;
z-index: 100;
.btn.btn-light:hover {
background: $color-white;

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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