From bb4349f3468fbcae64980b534cfd9e0624a426fb Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 24 Sep 2023 14:10:53 +0200 Subject: [PATCH 001/338] Initial label templates refactor [SCI-9303] --- app/assets/images/checkbox/checked.svg | 4 + app/assets/images/checkbox/default.svg | 4 + app/assets/images/checkbox/disabled.svg | 4 + app/assets/images/checkbox/indeterminate.svg | 4 + .../label_templates_datatable.js | 306 ------------------ app/assets/stylesheets/application.sass.scss | 2 + app/assets/stylesheets/experiments.scss | 1 + app/assets/stylesheets/projects.scss | 1 + app/assets/stylesheets/shared/ag_table.scss | 33 ++ app/assets/stylesheets/shared/cards.scss | 1 + .../stylesheets/shared/content_pane.scss | 7 +- app/assets/stylesheets/shared/select.scss | 1 - app/assets/stylesheets/tailwind/inputs.css | 2 +- app/controllers/label_templates_controller.rb | 6 +- app/datatables/custom_datatable_v2.rb | 71 ++++ app/datatables/label_template_datatable.rb | 62 ++-- app/javascript/packs/application.scss | 4 +- .../packs/vue/label_templates_table.js | 19 ++ app/javascript/vue/label_template/table.vue | 154 +++++++++ .../vue/shared/confirmation_modal.vue | 76 +++++ .../vue/shared/datatable/action_toolbar.vue | 72 +++++ .../vue/shared/datatable/pagination.vue | 57 ++++ app/javascript/vue/shared/datatable/table.vue | 237 ++++++++++++++ .../vue/shared/datatable/tableHeader.js | 40 +++ .../vue/shared/datatable/toolbar.vue | 82 +++++ app/javascript/vue/shared/select.vue | 17 +- .../toolbars/label_templates_service.rb | 11 +- .../label_templates/_delete_modal.html.erb | 20 -- .../label_templates/_index_toolbar.html.erb | 14 - app/views/label_templates/index.html.erb | 69 +--- config/initializers/assets.rb | 1 - config/locales/en.yml | 4 + config/routes.rb | 2 +- config/webpack/webpack.config.js | 8 +- package.json | 3 + yarn.lock | 22 ++ 36 files changed, 976 insertions(+), 445 deletions(-) create mode 100644 app/assets/images/checkbox/checked.svg create mode 100644 app/assets/images/checkbox/default.svg create mode 100644 app/assets/images/checkbox/disabled.svg create mode 100644 app/assets/images/checkbox/indeterminate.svg delete mode 100644 app/assets/javascripts/label_templates/label_templates_datatable.js create mode 100644 app/assets/stylesheets/shared/ag_table.scss create mode 100644 app/datatables/custom_datatable_v2.rb create mode 100644 app/javascript/packs/vue/label_templates_table.js create mode 100644 app/javascript/vue/label_template/table.vue create mode 100644 app/javascript/vue/shared/confirmation_modal.vue create mode 100644 app/javascript/vue/shared/datatable/action_toolbar.vue create mode 100644 app/javascript/vue/shared/datatable/pagination.vue create mode 100644 app/javascript/vue/shared/datatable/table.vue create mode 100644 app/javascript/vue/shared/datatable/tableHeader.js create mode 100644 app/javascript/vue/shared/datatable/toolbar.vue delete mode 100644 app/views/label_templates/_delete_modal.html.erb delete mode 100644 app/views/label_templates/_index_toolbar.html.erb diff --git a/app/assets/images/checkbox/checked.svg b/app/assets/images/checkbox/checked.svg new file mode 100644 index 0000000000..548f6ae2f7 --- /dev/null +++ b/app/assets/images/checkbox/checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/checkbox/default.svg b/app/assets/images/checkbox/default.svg new file mode 100644 index 0000000000..c5bd268486 --- /dev/null +++ b/app/assets/images/checkbox/default.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/checkbox/disabled.svg b/app/assets/images/checkbox/disabled.svg new file mode 100644 index 0000000000..903558dbdd --- /dev/null +++ b/app/assets/images/checkbox/disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/checkbox/indeterminate.svg b/app/assets/images/checkbox/indeterminate.svg new file mode 100644 index 0000000000..b1f9adf3e5 --- /dev/null +++ b/app/assets/images/checkbox/indeterminate.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/javascripts/label_templates/label_templates_datatable.js b/app/assets/javascripts/label_templates/label_templates_datatable.js deleted file mode 100644 index fb9696a0bd..0000000000 --- a/app/assets/javascripts/label_templates/label_templates_datatable.js +++ /dev/null @@ -1,306 +0,0 @@ -/* global I18n DataTableHelpers HelperModule */ -/* eslint-disable no-use-before-define no-param-reassign */ - -(function() { - 'use strict'; - - var LABEL_TEMPLATE_TABLE; - var rowsSelected = []; - - function rowsSelectedIDs() { - return rowsSelected.map(i => i.id); - } - - function renderCheckboxHTML(data) { - return `
- - -
`; - } - - function renderDefaultTemplateHTML(data) { - return data ? '' : ''; - } - - function renderNameHTML(data, type, row) { - return `
${data.icon_image_tag}${data.name}
`; - } - - function addAttributesToRow(row, data) { - $(row).addClass('label-template-row') - .attr('data-id', data['0']); - } - - function initNameClick() { - $('.label-info-link', '.dataTables_scrollBody').on('click', function() { - window.location.href = this.href; - return false; - }); - } - - function initToggleAllCheckboxes() { - $('input[name="select_all"]').change(function() { - if ($(this).is(':checked')) { - $("[data-action='toggle']").prop('checked', true); - $('.label-template-row').addClass('selected'); - $('.label-template-row [data-action="toggle"]').change(); - } else { - $("[data-action='toggle']").prop('checked', false); - $('.label-template-row').removeClass('selected'); - $('.label-template-row [data-action="toggle"]').change(); - } - }); - } - - function initCreateButton() { - $('#newLabelTemplate').on('click', function() { - $.post(this.dataset.url); - }); - } - - function initSetDefaultButton() { - $(document).on('click', '#setZplDefaultLabelTemplate, #setFluicsDefaultLabelTemplate', function(e) { - e.preventDefault(); - e.stopPropagation(); - - if (rowsSelected.length === 1) { - $.post(rowsSelected[0].setDefaultUrl, function(response) { - reloadTable(); - HelperModule.flashAlertMsg(response.message, 'success'); - }).fail((response) => { - HelperModule.flashAlertMsg(response.responseJSON.error, 'danger'); - }); - } - }); - } - - function initEditButton() { - $('#editTemplate').on('click', function() { - if (rowsSelected.length === 1) { - window.location.href = rowsSelected[0].editUrl; - } - }); - } - - function initDuplicateButton() { - $(document).on('click', '#duplicateLabelTemplate', function(e) { - e.preventDefault(); - e.stopPropagation(); - - if (rowsSelected.length > 0) { - $.post(this.dataset.url, { selected_ids: rowsSelectedIDs() }, function(response) { - reloadTable(); - HelperModule.flashAlertMsg(response.message, 'success'); - }).fail((response) => { - HelperModule.flashAlertMsg(response.responseJSON.error, 'danger'); - }); - } - }); - } - - function initDeleteModal() { - $(document).on('click', '#deleteLabelTemplate', function(e) { - e.preventDefault(); - e.stopPropagation(); - - $('#deleteLabelTemplatesModal').modal('show'); - }); - } - - function initDeleteButton() { - $('#confirmLabeleDeletion').on('click', function() { - if (rowsSelected.length > 0) { - $.post(this.dataset.url, { selected_ids: rowsSelectedIDs() }, function(response) { - reloadTable(); - HelperModule.flashAlertMsg(response.message, 'success'); - $('#deleteLabelTemplatesModal').modal('hide'); - }).fail((response) => { - HelperModule.flashAlertMsg(response.responseJSON.error, 'danger'); - $('#deleteLabelTemplatesModal').modal('hide'); - }); - } - }); - } - - function initRefreshFluicsButton() { - $('#syncFluicsTemplates').on('click', function() { - $.post(this.dataset.url, function(response) { - reloadTable(); - HelperModule.flashAlertMsg(response.message, 'success'); - }).fail((response) => { - HelperModule.flashAlertMsg(response.responseJSON.error, 'danger'); - }); - }); - } - - function tableDrawCallback() { - initToggleAllCheckboxes(); - initRowSelection(); - initNameClick(); - } - - function updateButtons() { - if (window.actionToolbarComponent) { - window.actionToolbarComponent.fetchActions({ label_template_ids: rowsSelectedIDs() }); - $('.dataTables_scrollBody').css('padding-bottom', `${rowsSelectedIDs().length > 0 ? 68 : 0}px`); - } - } - - function reloadTable() { - LABEL_TEMPLATE_TABLE.ajax.reload(null, false); - rowsSelected = []; - updateButtons(); - } - - function updateDataTableSelectAllCtrl() { - var $table = LABEL_TEMPLATE_TABLE.table().node(); - var $header = LABEL_TEMPLATE_TABLE.table().header(); - var $chkboxAll = $('.label-row-checkbox', $table); - var $chkboxChecked = $('.label-row-checkbox:checked', $table); - var chkboxSelectAll = $('input[name="select_all"]', $header).get(0); - - // If none of the checkboxes are checked - if ($chkboxChecked.length === 0) { - chkboxSelectAll.checked = false; - if ('indeterminate' in chkboxSelectAll) { - chkboxSelectAll.indeterminate = false; - } - - // If all of the checkboxes are checked - } else if ($chkboxChecked.length === $chkboxAll.length) { - chkboxSelectAll.checked = true; - if ('indeterminate' in chkboxSelectAll) { - chkboxSelectAll.indeterminate = false; - } - - // If some of the checkboxes are checked - } else { - chkboxSelectAll.checked = true; - if ('indeterminate' in chkboxSelectAll) { - chkboxSelectAll.indeterminate = true; - } - } - } - - function initRowSelection() { - // Handle clicks on checkbox - $('#label-templates-table').on('change', '.label-row-checkbox', function(ev) { - var rowId; - var index; - var row; - - rowId = this.dataset.labelTemplateId; - row = $(this).closest('tr')[0]; - - // Determine whether row ID is in the list of selected row IDs - index = rowsSelected.findIndex(v => v.id === rowId); - - // If checkbox is checked and row ID is not in list of selected row IDs - if (this.checked && index === -1) { - rowsSelected.push({ - id: rowId, - default: row.dataset.default, - editUrl: row.dataset.editUrl, - setDefaultUrl: row.dataset.setDefaultUrl, - format: row.dataset.format - }); - // Otherwise, if checkbox is not checked and row ID is in list of selected row IDs - } else if (!this.checked && index !== -1) { - rowsSelected.splice(index, 1); - } - - if (this.checked) { - $(this).closest('.label-template-row').addClass('selected'); - } else { - $(this).closest('.label-template-row').removeClass('selected'); - } - - updateDataTableSelectAllCtrl(); - - ev.stopPropagation(); - updateButtons(); - }); - } - // INIT - - function initDatatable() { - var $table = $('#label-templates-table'); - LABEL_TEMPLATE_TABLE = $table.DataTable({ - dom: "R<'label-toolbar'<'label-buttons-container'><'label-search-container'f>>t<'pagination-row hidden'<'pagination-info'li><'pagination-actions'p>>", - order: [[2, 'desc']], - stateSave: true, - sScrollX: '100%', - sScrollXInner: '100%', - processing: true, - serverSide: true, - ajax: $table.data('source'), - pagingType: 'simple_numbers', - colReorder: { - fixedColumnsLeft: 1000000 // Disable reordering - }, - columnDefs: [{ - targets: 0, - searchable: false, - orderable: false, - className: 'dt-body-center', - sWidth: '1%', - render: renderCheckboxHTML - }, { - targets: 1, - searchable: false, - orderable: true, - width: '1.5rem', - render: renderDefaultTemplateHTML - }, { - targets: 2, - className: 'label-template-name', - render: renderNameHTML - }, { - targets: 4, - className: 'whitespace-break-spaces', - render: data => `${data}` - }], - oLanguage: { - sSearch: I18n.t('general.filter') - }, - fnDrawCallback: tableDrawCallback, - createdRow: addAttributesToRow, - fnInitComplete: function() { - DataTableHelpers.initLengthAppearance($table.closest('.dataTables_wrapper')); - DataTableHelpers.initSearchField( - $table.closest('.dataTables_wrapper'), - I18n.t('label_templates.index.search_templates') - ); - $('.pagination-row').removeClass('hidden'); - - let toolBar = $($('#labelTemplatesToolbar').html()); - $('.label-buttons-container').html(toolBar); - - initCreateButton(); - initEditButton(); - initSetDefaultButton(); - initDuplicateButton(); - initDeleteModal(); - initRefreshFluicsButton(); - window.initActionToolbar(); - window.actionToolbarComponent.setBottomOffset(68); - }, - stateLoadParams: function(_, state) { - state.search.search = ''; - } - }); - } - - $('#wrapper').on('sideBar::shown sideBar::hidden', function() { - if (LABEL_TEMPLATE_TABLE) { - LABEL_TEMPLATE_TABLE.columns.adjust(); - } - }); - - initDatatable(); - initDeleteButton(); -}()); diff --git a/app/assets/stylesheets/application.sass.scss b/app/assets/stylesheets/application.sass.scss index cd047333fd..f7ac29dd3b 100644 --- a/app/assets/stylesheets/application.sass.scss +++ b/app/assets/stylesheets/application.sass.scss @@ -81,6 +81,7 @@ @import "shared/action_toolbar"; @import "shared/assets"; @import "shared/avatar"; +@import "shared/ag_table"; @import "shared/cards"; @import "shared/comments_sidebar"; @import "shared/comments"; @@ -111,6 +112,7 @@ @import "themes/repositories"; @import "themes/scinote"; + @import "navigation/general"; @import "navigation/breadcrumbs"; @import "navigation/left_menu"; diff --git a/app/assets/stylesheets/experiments.scss b/app/assets/stylesheets/experiments.scss index ae87469715..ba504315bc 100644 --- a/app/assets/stylesheets/experiments.scss +++ b/app/assets/stylesheets/experiments.scss @@ -22,6 +22,7 @@ } .projects-show { + --content-header-size: 9em; .content-header { height: var(--content-header-size); } diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss index 1bfd259c6c..60a1c18bb4 100644 --- a/app/assets/stylesheets/projects.scss +++ b/app/assets/stylesheets/projects.scss @@ -568,6 +568,7 @@ li.module-hover { // New projects page .projects-index { + --content-header-size: 9em; .content-header { height: var(--content-header-size); } diff --git a/app/assets/stylesheets/shared/ag_table.scss b/app/assets/stylesheets/shared/ag_table.scss new file mode 100644 index 0000000000..22cae419f2 --- /dev/null +++ b/app/assets/stylesheets/shared/ag_table.scss @@ -0,0 +1,33 @@ +.ag-root-wrapper { + --agg-row-border-color: var(--sn-light-grey); + --ag-odd-row-background-color: var(--sn-super-light-grey); + --ag-header-background-color: var(--sn-light-grey); + --ag-selected-row-background-color: var(--sn-super-light-blue); + --ag-range-selection-border: var(--sn-science-blue); + --ag-grid-size: .5rem; + --ag-cell-horizontal-padding: calc(var(--ag-grid-size) * 2); + --ag-row-hover-color: var(--sn-super-light-grey); + --ag-header-column-resize-handle-height: 100%; + --ag-header-column-resize-handle-color: var(--sn-sleepy-grey); + --ag-header-column-resize-handle-width: 1px; + --ag-row-border-width: 0px; + --ag-icon-font-code-checkbox-unchecked: asset-url("checkbox/default.svg"); + --ag-icon-font-code-checkbox-checked: asset-url("checkbox/checked.svg"); + --ag-icon-font-code-checkbox-indeterminate: asset-url("checkbox/indeterminate.svg"); + --ag-input-focus-box-shadow: none; + + border: 0; + + .ag-header { + border-bottom: 0; + } + + .ag-input-field-input { + cursor: pointer; + } + + .ag-input-field-input:focus { + outline: none !important; + outline-offset: 0 !important; + } +} diff --git a/app/assets/stylesheets/shared/cards.scss b/app/assets/stylesheets/shared/cards.scss index d0da8331e3..7dfc8d614c 100644 --- a/app/assets/stylesheets/shared/cards.scss +++ b/app/assets/stylesheets/shared/cards.scss @@ -3,6 +3,7 @@ // scss-lint:disable SelectorFormat .cards-wrapper { + --content-header-size: 9em; --card-min-width: 200px; --list-columns-number: 5; align-items: center; diff --git a/app/assets/stylesheets/shared/content_pane.scss b/app/assets/stylesheets/shared/content_pane.scss index c72fe6a684..ce83ad7ff2 100644 --- a/app/assets/stylesheets/shared/content_pane.scss +++ b/app/assets/stylesheets/shared/content_pane.scss @@ -2,7 +2,7 @@ // scss-lint:disable NestingDepth QualifyingElement .content-pane { - --content-header-size: 9.5em; + --content-header-size: 4em; background-color: var(--sn-white); margin: 20px 0; @@ -14,6 +14,11 @@ margin: 0; } + .fixed-content-body { + height: calc(100vh - var(--content-header-size) - var(--navbar-height)); + width: 100%; + } + .content-header { &.sticky-header { background-color: inherit; diff --git a/app/assets/stylesheets/shared/select.scss b/app/assets/stylesheets/shared/select.scss index 0ccf4fe3c4..0748fc2484 100644 --- a/app/assets/stylesheets/shared/select.scss +++ b/app/assets/stylesheets/shared/select.scss @@ -76,7 +76,6 @@ left: 0; max-height: 300px; overflow: hidden; - top: 2.5rem; width: 100%; z-index: 9999; } diff --git a/app/assets/stylesheets/tailwind/inputs.css b/app/assets/stylesheets/tailwind/inputs.css index 963aa94bd1..a5e40184ea 100644 --- a/app/assets/stylesheets/tailwind/inputs.css +++ b/app/assets/stylesheets/tailwind/inputs.css @@ -5,7 +5,7 @@ } .sci-input-container-v2 { - @apply relative h-[2.75rem] flex items-center; + @apply relative h-[2.75rem] flex items-center transition-all; } .sci-input-container-v2.input-sm { diff --git a/app/controllers/label_templates_controller.rb b/app/controllers/label_templates_controller.rb index 3a0d0613b8..c9b94a0b4b 100644 --- a/app/controllers/label_templates_controller.rb +++ b/app/controllers/label_templates_controller.rb @@ -50,13 +50,11 @@ def create label_template.last_modified_by = current_user label_template.save! log_activity(:label_template_created, label_template) - redirect_to label_template_path(label_template, new_label: true) + render json: { redirect_url: label_template_path(label_template, new_label: true) } end rescue StandardError => e Rails.logger.error(e.message) Rails.logger.error(e.backtrace.join("\n")) - flash[:error] = I18n.t('errors.general') - redirect_to label_templates_path end def update @@ -154,7 +152,7 @@ def actions_toolbar actions: Toolbars::LabelTemplatesService.new( current_user, - label_template_ids: params[:label_template_ids].split(',') + label_template_ids: params[:item_ids].split(',') ).actions } end diff --git a/app/datatables/custom_datatable_v2.rb b/app/datatables/custom_datatable_v2.rb new file mode 100644 index 0000000000..6d55810ef2 --- /dev/null +++ b/app/datatables/custom_datatable_v2.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +class CustomDatatableV2 + attr_reader :params, :options + + def initialize(view, raw_data, options = {}) + @raw_data = raw_data + @params = view.params + @options = options + end + + def as_json(_options = {}) + { + data: data, + pageTotal: records.total_pages + } + end + + def records + @records ||= fetch_records + end + + private + + def order_params + @order_params ||= + params.require(:order).permit(:column, :dir).to_h + end + + def fetch_records + records = get_raw_records + records = sort_records(records) if params[:order].present? + records = paginate_records(records) if params[:page].present? + records = filter_records(records) if params[:search].present? + records + end + + def paginate_records(records) + records.page(params[:page]).per(params[:per_page]) + end + + def sort_direction(order_params) + order_params[:dir] == 'asc' ? 'ASC' : 'DESC' + end + + def sort_records(records) + sort_by = "#{sortable_columns[order_params[:column].to_sym]} #{sort_direction(order_params)}" + records.order(sort_by) + end + + def generate_sortable_displayed_columns + @sortable_displayed_columns = [] + columns_params.each_value do |col| + @sortable_displayed_columns << col[:data] if col[:orderable] == 'true' + end + @sortable_displayed_columns + end + + def formated_date + f_date = I18n.backend.date_format.dup + f_date.gsub!(/%-d/, 'FMDD') + f_date.gsub!(/%d/, 'DD') + f_date.gsub!(/%-m/, 'FMMM') + f_date.gsub!(/%m/, 'MM') + f_date.gsub!(/%b/, 'Mon') + f_date.gsub!(/%B/, 'Month') + f_date.gsub!('%Y', 'YYYY') + f_date += ' HH24:MI' + f_date + end +end diff --git a/app/datatables/label_template_datatable.rb b/app/datatables/label_template_datatable.rb index da9928ce0b..83d67cfea5 100644 --- a/app/datatables/label_template_datatable.rb +++ b/app/datatables/label_template_datatable.rb @@ -1,24 +1,19 @@ # frozen_string_literal: true -class LabelTemplateDatatable < CustomDatatable +class LabelTemplateDatatable < CustomDatatableV2 include InputSanitizeHelper include Rails.application.routes.url_helpers - TABLE_COLUMNS = %w( - label_templates.default - label_templates.name - label_templates.type - label_templates.description - label_templates.modified_by - label_templates.updated_at - label_templates.created_by_user - label_templates.created_at - ).freeze - - def initialize(view, label_templates) - super(view) - @label_templates = label_templates - end + TABLE_COLUMNS = { + default: 'label_templates.default', + name: 'label_templates.name', + format: 'label_templates.type', + description: 'label_templates.description', + modified_by: 'label_templates.modified_by', + updated_at: 'label_templates.updated_at', + created_by: 'label_templates.created_by_user', + created_at: 'label_templates.created_at' + }.freeze def sortable_columns @sortable_columns ||= TABLE_COLUMNS @@ -30,24 +25,25 @@ def searchable_columns private + def order_params + @order_params ||= + params.require(:order).permit(:column, :dir).to_h + end + def data records.map do |record| { - '0' => record.id, - '1' => record.default, - '2' => append_format_icon(record), - '3' => escape_input(record.label_format), - '4' => escape_input(record.description), - '5' => escape_input(record.modified_by), - '6' => I18n.l(record.updated_at, format: :full), - '7' => escape_input(record.created_by_user), - '8' => I18n.l(record.created_at, format: :full), - 'recordInfoUrl' => '', - 'DT_RowAttr': { - 'data-edit-url': label_template_path(record), - 'data-set-default-url': set_default_label_template_path(record), - 'data-default': record.default, - 'data-format': record.label_format + id: record.id, + default: record.default, + name: append_format_icon(record), + format: escape_input(record.label_format), + description: escape_input(record.description), + modified_by: escape_input(record.modified_by), + updated_at: I18n.l(record.updated_at, format: :full), + created_by: escape_input(record.created_by_user), + created_at: I18n.l(record.created_at, format: :full), + attributes: { + edit_url: label_template_path(record) } } end @@ -65,7 +61,7 @@ def append_format_icon(record) end def get_raw_records - res = @label_templates.joins( + res = @raw_data.joins( 'LEFT OUTER JOIN users AS creators ' \ 'ON label_templates.created_by_id = creators.id' ).joins( @@ -85,7 +81,7 @@ def filter_records(records) records.where_attributes_like( ['label_templates.name', 'label_templates.label_format', 'label_templates.description', 'label_templates.modified_by', 'label_templates.created_by_user'], - dt_params.dig(:search, :value) + params[:search] ) end end diff --git a/app/javascript/packs/application.scss b/app/javascript/packs/application.scss index 848cb108a7..9180b881a3 100644 --- a/app/javascript/packs/application.scss +++ b/app/javascript/packs/application.scss @@ -1 +1,3 @@ -@import "bootstrap-select/sass/bootstrap-select" +@import "bootstrap-select/sass/bootstrap-select"; +@import "ag-grid-community/styles/ag-grid.css"; +@import "ag-grid-community/styles/ag-theme-alpine.css"; diff --git a/app/javascript/packs/vue/label_templates_table.js b/app/javascript/packs/vue/label_templates_table.js new file mode 100644 index 0000000000..f9a1d85323 --- /dev/null +++ b/app/javascript/packs/vue/label_templates_table.js @@ -0,0 +1,19 @@ +import TurbolinksAdapter from 'vue-turbolinks'; +import Vue from 'vue/dist/vue.esm'; +import PerfectScrollbar from 'vue2-perfect-scrollbar'; +import LabelTemplatesTable from '../../vue/label_template/table.vue'; + +Vue.use(TurbolinksAdapter); +Vue.use(PerfectScrollbar); +Vue.prototype.i18n = window.I18n; + +window.initLabelTemplatesTableComponent = () => { + new Vue({ + el: '#labelTemplatesTable', + components: { + 'label-templates-table': LabelTemplatesTable, + }, + }); +}; + +initLabelTemplatesTableComponent(); diff --git a/app/javascript/vue/label_template/table.vue b/app/javascript/vue/label_template/table.vue new file mode 100644 index 0000000000..275fd8ee6b --- /dev/null +++ b/app/javascript/vue/label_template/table.vue @@ -0,0 +1,154 @@ + + + diff --git a/app/javascript/vue/shared/confirmation_modal.vue b/app/javascript/vue/shared/confirmation_modal.vue new file mode 100644 index 0000000000..19b6f6498e --- /dev/null +++ b/app/javascript/vue/shared/confirmation_modal.vue @@ -0,0 +1,76 @@ + + \ No newline at end of file diff --git a/app/javascript/vue/shared/datatable/action_toolbar.vue b/app/javascript/vue/shared/datatable/action_toolbar.vue new file mode 100644 index 0000000000..9b916fc0c0 --- /dev/null +++ b/app/javascript/vue/shared/datatable/action_toolbar.vue @@ -0,0 +1,72 @@ + + + diff --git a/app/javascript/vue/shared/datatable/pagination.vue b/app/javascript/vue/shared/datatable/pagination.vue new file mode 100644 index 0000000000..0db05e1a72 --- /dev/null +++ b/app/javascript/vue/shared/datatable/pagination.vue @@ -0,0 +1,57 @@ + + + diff --git a/app/javascript/vue/shared/datatable/table.vue b/app/javascript/vue/shared/datatable/table.vue new file mode 100644 index 0000000000..8541e39804 --- /dev/null +++ b/app/javascript/vue/shared/datatable/table.vue @@ -0,0 +1,237 @@ + + + diff --git a/app/javascript/vue/shared/datatable/tableHeader.js b/app/javascript/vue/shared/datatable/tableHeader.js new file mode 100644 index 0000000000..602796479e --- /dev/null +++ b/app/javascript/vue/shared/datatable/tableHeader.js @@ -0,0 +1,40 @@ +export default { + template: ` +
+
+
{{ params.displayName }}
+
+ +
+
+ +
+
+ `, + data() { + return { + activeSort: null, + }; + }, + beforeMount() {}, + mounted() { + this.params.column.addEventListener('sortChanged', this.onSortChanged); + this.onSortChanged(); + }, + methods: { + onSortChanged() { + this.activeSort = null; + if (this.params.column.isSortAscending()) { + this.activeSort = 'asc'; + } else if (this.params.column.isSortDescending()) { + this.activeSort = 'desc'; + } + }, + + onSortRequested(order, event) { + if (!this.params.enableSorting) return; + + this.params.setSort(order, event.shiftKey); + }, + }, +}; diff --git a/app/javascript/vue/shared/datatable/toolbar.vue b/app/javascript/vue/shared/datatable/toolbar.vue new file mode 100644 index 0000000000..3a50a6bfd4 --- /dev/null +++ b/app/javascript/vue/shared/datatable/toolbar.vue @@ -0,0 +1,82 @@ + + + diff --git a/app/javascript/vue/shared/select.vue b/app/javascript/vue/shared/select.vue index 2077200ab8..824efdd815 100644 --- a/app/javascript/vue/shared/select.vue +++ b/app/javascript/vue/shared/select.vue @@ -116,8 +116,10 @@ updateOptionPosition() { const container = $(this.$refs.container); const rect = container.get(0).getBoundingClientRect(); + const screenHeight = window.innerHeight; let width = rect.width; let top = rect.top + rect.height; + let bottom = screenHeight - rect.bottom + rect.height; let left = rect.left; const modal = container.parents('.modal-content'); @@ -125,10 +127,23 @@ if (modal.length > 0) { const modalRect = modal.get(0).getBoundingClientRect(); top -= modalRect.top; + bottom -= modalRect.bottom; left -= modalRect.left; } - this.optionPositionStyle = `position: fixed; top: ${top}px; left: ${left}px; width: ${width}px` + if (rect.bottom > screenHeight / 2) { + this.optionPositionStyle = ` + position: fixed; + bottom: ${bottom}px; + left: ${left}px; + width: ${width}px` + } else { + this.optionPositionStyle = ` + position: fixed; + top: ${top}px; + left: ${left}px; + width: ${width}px` + } }, setUpBlurHandlers() { setTimeout(() => { diff --git a/app/services/toolbars/label_templates_service.rb b/app/services/toolbars/label_templates_service.rb index 3dae82c71c..f0baa4c4a0 100644 --- a/app/services/toolbars/label_templates_service.rb +++ b/app/services/toolbars/label_templates_service.rb @@ -56,8 +56,8 @@ def set_as_default_action name: 'set_as_default', label: I18n.t("label_templates.index.toolbar.set_#{@label_templates.first.type}_default"), icon: 'fas fa-thumbtack', - button_id: 'setZplDefaultLabelTemplate', - type: :legacy + path: set_default_label_template_path(@label_templates.first), + type: :emit } end @@ -70,9 +70,8 @@ def duplicate_action name: 'duplicate', label: I18n.t('label_templates.index.toolbar.duplicate'), icon: 'sn-icon sn-icon-duplicate', - button_id: 'duplicateLabelTemplate', path: duplicate_label_templates_path, - type: :legacy + type: :emit } end @@ -87,8 +86,8 @@ def delete_action name: 'delete', label: I18n.t('label_templates.index.toolbar.delete'), icon: 'sn-icon sn-icon-delete', - button_id: 'deleteLabelTemplate', - type: :legacy + path: delete_label_templates_path, + type: :emit } end end diff --git a/app/views/label_templates/_delete_modal.html.erb b/app/views/label_templates/_delete_modal.html.erb deleted file mode 100644 index 26822227b1..0000000000 --- a/app/views/label_templates/_delete_modal.html.erb +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/app/views/label_templates/_index_toolbar.html.erb b/app/views/label_templates/_index_toolbar.html.erb deleted file mode 100644 index 5aa8bc1fa6..0000000000 --- a/app/views/label_templates/_index_toolbar.html.erb +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/app/views/label_templates/index.html.erb b/app/views/label_templates/index.html.erb index ee499e6c68..29646202bd 100644 --- a/app/views/label_templates/index.html.erb +++ b/app/views/label_templates/index.html.erb @@ -1,60 +1,23 @@ <% if current_team %> <% provide(:sidebar_title, t('sidebar.templates.sidebar_title')) %> <% provide(:container_class, "no-second-nav-container") %> - <%= content_for :sidebar do %> - <%= render partial: "/shared/sidebar/templates_sidebar", locals: {active: :label} %> - <% end %> - - <% content_for :head do %> - - <% end %> - - <%= stylesheet_link_tag 'datatables' %> - - -
-