Add Label template list screen - view mode [SCI-7018] (#4292)

* Initial label template datatable [SCI-7018]

* Add new columns to LabelTemplate and update datatable view [SCI-7018]

* Fix after rebase [SCI-7018]

* Fix migration, disable checkboxes in view mode and fix label template to team level [SCI-7018]
This commit is contained in:
ajugo 2022-07-27 10:10:32 +02:00 committed by GitHub
parent 118ae55f2f
commit ad4f52d912
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 400 additions and 4 deletions

View file

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2315 4.00001L13.9001 8.00001L11.2312 12H1V12H0V12C0 12.2652 0.105357 12.5196 0.292893 12.7071C0.48043 12.8947 0.734784 13 1 13H11.2312C11.3957 13.0006 11.5577 12.9604 11.7028 12.8829C11.8479 12.8055 11.9715 12.6932 12.0625 12.5563L14.9187 8.27501C14.9741 8.19398 15.0037 8.09814 15.0037 8.00001C15.0037 7.90188 14.9741 7.80603 14.9187 7.72501L12.0625 3.44376C11.9715 3.30678 11.8479 3.19454 11.7028 3.11709C11.5577 3.03965 11.3957 2.99941 11.2312 3.00001H1C0.734784 3.00001 0.48043 3.10536 0.292893 3.2929C0.105358 3.48043 1.7995e-06 3.73479 0 4H1V4.00001L11.2315 4.00001Z" fill="#A0A0A8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.35297e-06 5.5C2.35297e-06 5.22386 0.22386 5 0.500002 5H2.5C2.66591 5 2.82102 5.0823 2.91404 5.21969C3.00705 5.35707 3.02586 5.53165 2.96424 5.6857L1.23852 10H2.5C2.77614 10 3 10.2239 3 10.5C3 10.7761 2.77614 11 2.5 11H0.500002C0.33409 11 0.178984 10.9177 0.0859687 10.7803C-0.00704625 10.6429 -0.0258541 10.4683 0.035764 10.3143L1.76149 6H0.500002C0.22386 6 2.35297e-06 5.77614 2.35297e-06 5.5ZM10.5 11C10.775 11 11 10.775 11 10.5C11 10.225 10.775 10 10.5 10H9V5.5C9 5.225 8.775 5 8.5 5C8.225 5 8 5.225 8 5.5V10.5C8 10.775 8.225 11 8.5 11H10.5ZM7 7C7 6.03437 6.21563 5 5.25 5H4.5C4.22375 5 4 5.225 4 5.5V10.5C4 10.775 4.22375 11 4.5 11C4.77625 11 5 10.775 5 10.5V9H5.25C6.21563 9 7 7.96563 7 7ZM5.25 6C5.66563 6 6 6.58438 6 7C6 7.41563 5.66563 8 5.25 8H5V6H5.25Z" fill="#404048"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,107 @@
/* global I18n DataTableHelpers */
(function() {
'use strict';
var LABEL_TEMPLATE_TABLE;
function renderCheckboxHTML(data, type, row) {
return `<div class="sci-checkbox-container">
<input type="checkbox" class="sci-checkbox" data-action='toggle'
data-label-template-id="${data}" ${row.manage_permission ? '' : 'disabled'}>
<span class="sci-checkbox-label"></span>
</div>`;
}
function renderDefaultTemplateHTML(data) {
return data ? '<i class="fas fa-thumbtack"></i>' : '';
}
function renderNameHTML(data, type, row) {
return `${data.icon_url}<a href='${row.recordInfoUrl}' class='record-info-link'>${data.name}</a>`;
}
function addAttributesToRow(row, data) {
$(row).addClass('label-template-row')
.attr('data-id', data['0']);
}
function checkboxToggleCallback() {
$("[data-action='toggle']").change(function() {
if ($(this).is(':checked')) {
$(this).closest('.label-template-row').addClass('selected');
} else {
$(this).closest('.label-template-row').removeClass('selected');
}
});
}
function initToggleAllCheckboxes() {
$('input[name="select_all"]').change(function() {
if ($(this).is(':checked')) {
$("[data-action='toggle']").prop('checked', true);
$('.label-template-row').addClass('selected');
} else {
$("[data-action='toggle']").prop('checked', false);
$('.label-template-row').removeClass('selected');
}
});
}
function tableDrowCallback() {
checkboxToggleCallback();
initToggleAllCheckboxes();
}
// INIT
function initDatatable() {
var $table = $('#label-templates-table');
LABEL_TEMPLATE_TABLE = $table.DataTable({
dom: "Rt<'pagination-row hidden'<'pagination-info'li><'pagination-actions'p>>",
order: [[2, 'desc']],
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: false,
sWidth: '1%',
render: renderDefaultTemplateHTML
}, {
targets: 2,
className: 'label-template-name',
render: renderNameHTML
}],
oLanguage: {
sSearch: I18n.t('general.filter')
},
fnDrawCallback: tableDrowCallback,
createdRow: addAttributesToRow,
fnInitComplete: function() {
DataTableHelpers.initLengthAppearance($table.closest('.dataTables_wrapper'));
$('.pagination-row').removeClass('hidden');
}
});
}
$('.label-templates-index').on('keyup', '.label-templates-search', function() {
LABEL_TEMPLATE_TABLE.search($(this).val()).draw();
});
initDatatable();
}());

View file

@ -0,0 +1,54 @@
// scss-lint:disable SelectorDepth NestingDepth IdSelector SelectorFormat
.label-templates-index {
.search-label-templates-container {
float: right;
margin-right: 2em;
padding-bottom: 16px;
width: 200px;
.fa-search {
padding-bottom: 16px;
}
}
.toolbar-row.label-templates-toolbar {
border-bottom: 0;
}
}
.label-templates-datatable {
height: calc(100vh - var(--navbar-height) - var(--content-header-size));
#label-templates-table_wrapper {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
.dataTables_scroll {
display: flex;
flex-direction: column;
flex-grow: 1;
height: calc(100% - var(--datatable-pagination-row));
.dataTables_scrollHead {
flex-shrink: 0;
}
}
.pagination-row {
flex-shrink: 0;
}
}
}
.label-template-name {
align-items: center;
display: flex;
}
.label-template-icon {
padding-bottom: 2px;
padding-right: 4px;
}

View file

@ -1,15 +1,38 @@
# frozen_string_literal: true
class LabelTemplatesController < ApplicationController
include InputSanitizeHelper
before_action :check_view_permissions
before_action :load_label_templates, only: %i(index datatable)
layout 'fluid'
def index; end
def datatable
respond_to do |format|
format.json do
render json: ::LabelTemplateDatatable.new(
view_context,
can_manage_label_templates?(current_team),
@label_templates
)
end
end
end
def new
render_404
end
private
def check_view_permissions
render_403 unless can_view_label_templates?(current_team)
end
def load_label_templates
@label_templates = LabelTemplate.where(team_id: current_team.id)
end
end

View file

@ -0,0 +1,76 @@
# frozen_string_literal: true
class LabelTemplateDatatable < CustomDatatable
include InputSanitizeHelper
include Rails.application.routes.url_helpers
TABLE_COLUMNS = %w(
label_templates.name
label_templates.format
label_templates.description
label_templates.modified_by
label_templates.updated_at
label_templates.created_by_user
label_templates.created_at
).freeze
def initialize(view, can_manage_templates, label_templates)
super(view)
@manage_template = can_manage_templates
@label_templates = label_templates
end
def sortable_columns
@sortable_columns ||= TABLE_COLUMNS
end
def searchable_columns
@searchable_columns ||= TABLE_COLUMNS
end
private
def data
records.map do |record|
{
'0' => record.id,
'1' => record.default,
'2' => append_format_icon(sanitize_input(record.name)),
'3' => sanitize_input(record.format),
'4' => sanitize_input(record.description),
'5' => sanitize_input(record.modified_by),
'6' => I18n.l(record.updated_at, format: :full),
'7' => sanitize_input(record.created_by_user),
'8' => I18n.l(record.created_at, format: :full),
'recordInfoUrl' => '',
'manage_permission' => @manage_template
}
end
end
def append_format_icon(data)
{ icon_url: ActionController::Base.helpers.image_tag('label_template_icons/zpl.svg', class: 'label-template-icon'),
name: data }
end
def get_raw_records
res = @label_templates.joins(
'LEFT OUTER JOIN users AS creators ' \
'ON label_templates.created_by_id = creators.id'
).joins(
'LEFT OUTER JOIN users AS modifiers '\
'ON label_templates.last_modified_by_id = modifiers.id'
).select('label_templates.* AS label_templates')
.select('creators.full_name AS created_by_user')
.select('modifiers.full_name AS modified_by')
LabelTemplate.from(res, :label_templates)
end
def filter_records(records)
records.where_attributes_like(
['label_templates.name', 'label_templates.format', 'label_templates.description',
'label_templates.modified_by', 'label_templates.created_by_user'],
dt_params.dig(:search, :value)
)
end
end

View file

@ -1,8 +1,11 @@
# frozen_string_literal: true
class LabelTemplate < ApplicationRecord
include SearchableModel
enum language_type: { zpl: 0 }
validates :name, presence: true
validates :name, presence: true, length: { minimum: Constants::NAME_MIN_LENGTH,
maximum: Constants::NAME_MAX_LENGTH }
validates :size, presence: true
validates :content, presence: true

View file

@ -60,6 +60,10 @@ Canaid::Permissions.register_for(Team) do
can :view_label_templates do |user, team|
user.is_normal_user_or_admin_of_team?(team)
end
can :manage_label_templates do |user, team|
user.is_admin_of_team?(team)
end
end
Canaid::Permissions.register_for(ProjectFolder) do

View file

@ -1,8 +1,68 @@
<% 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 %>
<div class="content-pane flexible">
<% content_for :head do %>
<meta name="turbolinks-cache-control" content="no-cache">
<% end %>
<%= stylesheet_link_tag 'datatables' %>
<div class="content-pane flexible label-templates-index">
<div class="content-header sticky-header">
<div class="title-row">
<h1>
<%= t('label_templates.index.head_title') %>
</h1>
</div>
<div id="toolbarWrapper" class="toolbar-row label-templates-toolbar">
<div class="sci-input-container left-icon search-label-templates-container">
<input type="text"
class="sci-input-field label-templates-search"
placeholder="<%= t("label_templates.index.search_templates") %>">
</input>
<i class="fas fa-search"></i>
</div>
</div>
<div id="content-label-templates-index">
<div class="row">
<div class="col-md-12">
<div class="label-templates-datatable">
<table id="label-templates-table"
class="table"
data-source="<%= label_templates_datatable_path(format: :json) %>">
<thead>
<tr>
<th id="select-all">
<div class="sci-checkbox-container">
<input name="select_all" type="checkbox" class="sci-checkbox"
<%= can_manage_label_templates?(current_team) ? '' : 'disabled' %>>
<span class="sci-checkbox-label"></span>
</div>
</th>
<th id="label-template-selected"><i class="fas fa-thumbtack"></i></th>
<th id="label-template-name"><%= t('label_templates.index.thead_name') %></th>
<th id="label-template-format"><%= t('label_templates.index.format') %></th>
<th id="label-template-description"><%= t('label_templates.index.description') %></th>
<th id="label-template-updated-by"><%= t('label_templates.index.updated_by') %></th>
<th id="label-template-updated-at"><%= t('label_templates.index.updated_at') %></th>
<th id="label-template-created-by"><%= t('label_templates.index.created_by') %></th>
<th id="label-template-created-at"><%= t('label_templates.index.created_at') %></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<%= javascript_include_tag 'label_templates/label_templates_datatable' %>
<% end %>

View file

@ -91,6 +91,7 @@ Rails.application.config.assets.precompile += %w(sidebar_toggle.js)
Rails.application.config.assets.precompile += %w(reports/reports_datatable.js)
Rails.application.config.assets.precompile += %w(reports/save_pdf_to_inventory.js)
Rails.application.config.assets.precompile += %w(reports/content.js)
Rails.application.config.assets.precompile += %w(label_templates/label_templates_datatable.js)
# Libraries needed for Handsontable formulas
Rails.application.config.assets.precompile += %w(jquery.js)

View file

@ -809,6 +809,19 @@ en:
select_user_role: "Please select a user role."
add_user_generic_error: "An error occurred. "
can_add_user_to_project: "Can not add user to the project."
label_templates:
index:
head_title: 'Label templates'
search_templates: 'Filter templates'
thead_name: 'Name'
format: 'Format'
description: 'Description'
updated_by: 'Last updated by'
updated_at: 'Last updated on'
created_by: 'Created by'
created_at: 'Created on'
label_printers:
create:

View file

@ -45,6 +45,9 @@ Rails.application.routes.draw do
to: 'users/settings/account/addons#index',
as: 'addons'
resources :label_templates, only: %i(index new)
get 'label_templates/datatable', to: 'label_templates#datatable'
resources :label_printers, except: :show, path: 'users/settings/account/addons/label_printers' do
post :create_fluics, on: :collection
end
@ -54,8 +57,6 @@ Rails.application.routes.draw do
get :update_progress_modal, on: :member
end
resources :label_templates
get 'users/settings/account/connected_accounts',
to: 'users/settings/account/connected_accounts#index',
as: 'connected_accounts'

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
class AddColumnsToLabelTemplates < ActiveRecord::Migration[6.1]
def up
change_table :label_templates, bulk: true do |t|
t.string :description
t.string :format, null: false, default: 'ZPL'
t.integer :last_modified_by_id
t.integer :created_by_id
end
add_foreign_key :label_templates, :users, column: :last_modified_by_id
add_foreign_key :label_templates, :users, column: :created_by_id
add_index :label_templates, :last_modified_by_id
add_index :label_templates, :created_by_id
add_reference :label_templates, :team, index: true, foreign_key: true
if Team.count.positive?
LabelTemplate.first.update(
created_by_id: Team.first.created_by_id,
last_modified_by_id: Team.first.created_by_id,
team_id: Team.first.id
)
end
end
def down
remove_reference :label_templates, :team, index: true, foreign_key: true
remove_index :label_templates, column: :last_modified_by_id
remove_index :label_templates, column: :created_by_id
remove_foreign_key :label_templates, :users, column: :last_modified_by_id
remove_foreign_key :label_templates, :users, column: :created_by_id
change_table :label_templates, bulk: true do |t|
t.remove :description
t.remove :format, null: false, default: 'ZPL'
t.remove :last_modified_by_id
t.remove :created_by_id
end
end
end

View file

@ -29,4 +29,12 @@ if User.count.zero?
[],
Extends::INITIAL_USER_OPTIONS
)
if LabelTemplate.count.positive?
LabelTemplate.first.update(
created_by_id: Team.first.created_by_id,
last_modified_by_id: Team.first.created_by_id,
team_id: Team.first.id
)
end
end