Merge pull request #4311 from aignatov-bio/ai-sci-7030-label-template-view-edit-screen

Add general layout for template view screen [SCI-7030]
This commit is contained in:
aignatov-bio 2022-08-04 14:13:25 +02:00 committed by GitHub
commit e5448f3d1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 390 additions and 33 deletions

View file

@ -23,7 +23,11 @@
}
function renderNameHTML(data, type, row) {
return `${data.icon_url}<a href='${row.recordInfoUrl}' class='record-info-link'>${data.name}</a>`;
return `${data.icon_url}<a
href='${row.DT_RowAttr['data-edit-url']}'
class='record-info-link'
onclick='window.open(this.href, "_self")'
>${data.name}</a>`;
}
function addAttributesToRow(row, data) {
@ -43,11 +47,9 @@
});
}
function initEditButton() {
$('#editLabelTemplate').on('click', function() {
if (rowsSelected.length === 1) {
window.open(editUrl, '_blank');
}
function initCreateButton() {
$('#newLabelTemplate').on('click', function() {
$.post(this.dataset.url);
});
}
@ -249,7 +251,7 @@
let toolBar = $($('#labelTemplatesToolbar').html());
$('.label-buttons-container').html(toolBar);
initEditButton();
initCreateButton();
initSetDefaultButton();
initDuplicateButton();
initDeleteModal();

View file

@ -0,0 +1,89 @@
// scss-lint:disable SelectorDepth NestingDepth ImportantRule
.label-templates-show {
.title-row {
height: unset !important;
.label-template-icon {
flex-shrink: 0;
margin-bottom: auto;
margin-right: 5px;
margin-top: 13px;
width: 32px;
}
.sci-inline-edit {
@include font-h1;
flex-grow: 1;
max-width: calc(100% - 50px);
}
}
.template-descripiton {
@include font-button;
margin: 1em 0;
.title {
font-weight: bold;
}
}
.label-template-container {
display: flex;
.title {
font-weight: bold;
}
.label-edit-container {
flex-basis: 60%;
padding-right: 1em;
.button-container {
display: flex;
.refresh-preview {
margin-right: auto;
}
.save-template {
margin-left: .25em;
}
}
}
.label-preview-container {
flex-basis: 40%;
padding-left: 1em;
}
}
.label-view-container {
cursor: pointer;
padding: .5em;
position: relative;
white-space: pre;
.fa-pen {
display: none;
padding: 1em;
position: absolute;
right: 0;
top: 0;
}
&:hover {
background-color: $color-concrete;
.fa-pen {
display: inline-block;
}
}
}
.label-textarea {
min-height: 240px;
padding: .5em;
width: 100%;
}
}

View file

@ -36,7 +36,8 @@
}
}
textarea {
textarea,
input {
background: transparent;
border: 0;
min-height: 1em;

View file

@ -5,9 +5,9 @@ class LabelTemplatesController < ApplicationController
before_action :check_feature_enabled
before_action :check_view_permissions, only: %i(index datatable)
before_action :check_manage_permissions, only: %i(new duplicate set_default delete)
before_action :check_manage_permissions, only: %i(create duplicate set_default delete update)
before_action :load_label_templates, only: %i(index datatable)
before_action :load_label_template, only: %i(edit set_default)
before_action :load_label_template, only: %i(show set_default update)
layout 'fluid'
@ -25,9 +25,33 @@ class LabelTemplatesController < ApplicationController
end
end
def new; end
def show
respond_to do |format|
format.json { render json: @label_template, serializer: LabelTemplateSerializer, user: current_user }
format.html
end
end
def edit; end
def create
label_template = LabelTemplate.create!(
team_id: current_team.id,
name: I18n.t('label_templates.new_label_template'),
language_type: :zpl,
format: 'ZPL',
size: '1" x 0.5" / 25.4mm x 12.7mm',
content: Extends::DEFAULT_LABEL_TEMPLATE[:zpl]
)
redirect_to label_template_path(label_template, new_label: true)
end
def update
if @label_template.update(label_template_params)
render json: @label_template, serializer: LabelTemplateSerializer, user: current_user
else
render json: { error: @label_template.errors.messages }, status: :unprocessable_entity
end
end
def duplicate
ActiveRecord::Base.transaction do
@ -89,4 +113,8 @@ class LabelTemplatesController < ApplicationController
def load_label_template
@label_template = LabelTemplate.where(team_id: current_team.id).find(params[:id])
end
def label_template_params
params.require(:label_template).permit(:name, :description, :content)
end
end

View file

@ -44,7 +44,7 @@ class LabelTemplateDatatable < CustomDatatable
'8' => I18n.l(record.created_at, format: :full),
'recordInfoUrl' => '',
'DT_RowAttr': {
'data-edit-url': edit_label_template_path(record),
'data-edit-url': label_template_path(record),
'data-set-default-url': set_default_label_template_path(record)
},
'manage_permission' => @manage_template

View file

@ -0,0 +1,25 @@
import TurbolinksAdapter from 'vue-turbolinks';
import Vue from 'vue/dist/vue.esm';
import LabelTemplateContainer from '../../vue/label_template/container.vue';
Vue.use(TurbolinksAdapter);
Vue.prototype.i18n = window.I18n;
window.initLabelTemplateComponent = () => {
new Vue({
el: '#labelTemplateContainer',
components: {
'label-template-container': LabelTemplateContainer
},
data() {
return {
labelTemplateUrl: $('#labelTemplateContainer').data('label-template-url'),
labelTemplatesUrl: $('#labelTemplateContainer').data('label-templates-url'),
newLabel: $('#labelTemplateContainer').data('new-label')
};
}
});
};
initLabelTemplateComponent();

View file

@ -0,0 +1,152 @@
<template>
<div class="content-pane flexible label-templates-show">
<div class="content-header">
<div id="breadcrumbsWrapper">
<div class="breadcrumbs-container">
<a :href="labelTemplatesUrl" class="breadcrumbs-link">
{{ i18n.t('label_templates.show.breadcrumb_index') }}
</a>
<span class="delimiter">/</span>
</div>
</div>
<div v-if="labelTemplate.id" class="title-row">
<img :src="labelTemplate.attributes.icon_url" class="label-template-icon"/>
<InlineEdit
:value="labelTemplate.attributes.name"
:characterLimit="255"
:allowBlank="false"
:attributeName="i18n.t('label_templates.show.name_error_prefix')"
:autofocus="editingName"
:editOnload="newLabel"
@editingEnabled="editingName = true"
@editingDisabled="editingName = false"
@update="updateName"
/>
</div>
</div>
<div id="content-label-templates-show">
<div v-if="labelTemplate.id" class="template-descripiton">
<div class="title">
{{ i18n.t('label_templates.show.description_title') }}
</div>
<InlineEdit
:value="labelTemplate.attributes.description"
:characterLimit="255"
:allowBlank="true"
:attributeName="i18n.t('label_templates.show.description_error_prefix')"
:placeholder="i18n.t('label_templates.show.description_placeholder')"
:autofocus="editingDescription"
@editingEnabled="editingDescription = true"
@editingDisabled="editingDescription = false"
@update="updateDescription"
/>
</div>
<div v-if="labelTemplate.id" class="label-template-container">
<div class="label-edit-container">
<div class="title">
{{ i18n.t('label_templates.show.content_title', { format: labelTemplate.attributes.language_type.toUpperCase() }) }}
</div>
<template v-if="editingContent">
<div class="label-textarea-container">
<textarea
ref="contentInput"
v-model="newContent"
class="label-textarea"
@blur="updateContent"
></textarea>
</div>
<div class="button-container">
<div class="btn btn-secondary refresh-preview">
<i class="fas fa-sync"></i>
{{ i18n.t('label_templates.show.buttons.refresh') }}
</div>
<div class="btn btn-secondary" @click="editingContent = false">
{{ i18n.t('general.cancel') }}
</div>
<div class="btn btn-primary save-template" @click="updateContent">
<i class="fas fa-save"></i>
{{ i18n.t('label_templates.show.buttons.save') }}
</div>
</div>
</template>
<div v-else class="label-view-container" :title="i18n.t('label_templates.show.view_content_tooltip')" @click="editingContent = true">{{ labelTemplate.attributes.content}}
<i class="fas fa-pen"></i>
</div>
</div>
<div class="label-preview-container">
<div class="title">
{{ i18n.t('label_templates.show.preview_title') }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import InlineEdit from 'vue/shared/inline_edit.vue'
export default {
name: 'LabelTemplateContainer',
props: {
labelTemplateUrl: String,
labelTemplatesUrl: String,
newLabel: Boolean
},
data() {
return {
labelTemplate: {
attributes: {}
},
editingName: false,
editingDescription: false,
editingContent: false,
newContent: ''
}
},
components: {InlineEdit},
created() {
console.log(this.newLabel)
$.get(this.labelTemplateUrl, (result) => {
this.labelTemplate = result.data
this.newContent = this.labelTemplate.attributes.content
})
},
methods: {
updateName(newName) {
$.ajax({
url: this.labelTemplate.attributes.urls.update,
type: 'PATCH',
data: {label_template: {name: newName}},
success: (result) => {
this.labelTemplate.attributes.name = result.data.attributes.name
}
});
},
updateDescription(newDescription) {
$.ajax({
url: this.labelTemplate.attributes.urls.update,
type: 'PATCH',
data: {label_template: {description: newDescription}},
success: (result) => {
this.labelTemplate.attributes.description = result.data.attributes.description
}
});
},
updateContent() {
$.ajax({
url: this.labelTemplate.attributes.urls.update,
type: 'PATCH',
data: {label_template: {content: this.newContent}},
success: (result) => {
this.labelTemplate.attributes.content = result.data.attributes.content
this.editingContent = false
}
});
},
}
}
</script>

View file

@ -21,6 +21,13 @@ class LabelTemplate < ApplicationRecord
end
end
def icon_url
case language_type
when 'zpl'
'/images/label_template_icons/zpl.svg'
end
end
private
def default_template

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
class LabelTemplateSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
attributes :name, :description, :language_type, :icon_url, :urls, :content
def urls
{
update: label_template_path(object)
}
end
end

View file

@ -1,14 +1,10 @@
<template id="labelTemplatesToolbar">
<% if can_manage_label_templates?(current_team) %>
<a href="<%= new_label_template_path %>" title="<%= t('label_templates.index.toolbar.new') %>"
class="btn btn-primary auto-shrink-button" id="newLabelTemplate" target="_blank">
<button data-url="<%= label_templates_path %>" title="<%= t('label_templates.index.toolbar.new') %>"
class="btn btn-primary auto-shrink-button" id="newLabelTemplate">
<i class="fas fa-plus"></i>
<span class="button-text"><%= t('label_templates.index.toolbar.new') %></span>
</a>
<div class="btn btn-light hidden selected-actions auto-shrink-button" id="editLabelTemplate" title="<%= t('label_templates.index.toolbar.edit') %>">
<i class="fas fa-pen"></i>
<span class="button-text"><%= t('label_templates.index.toolbar.edit') %></span>
</div>
</button>
<div class="btn btn-light hidden selected-actions auto-shrink-button"
title="<%= t('label_templates.index.toolbar.duplicate') %>"
data-url="<%= duplicate_label_templates_path %>"

View file

@ -1,5 +0,0 @@
<% 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 %>

View file

@ -25,7 +25,7 @@
<div class="label-templates-datatable">
<table id="label-templates-table"
class="table"
data-source="<%= label_templates_datatable_path(format: :json) %>">
data-source="<%= datatable_label_templates_path(format: :json) %>">
<thead>
<tr>
<th id="select-all">

View file

@ -1,5 +0,0 @@
<% 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 %>

View file

@ -0,0 +1,19 @@
<% 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 id="labelTemplateContainer"
data-label-template-url="<%= label_template_path(params[:id], format: :json) %>"
data-label-templates-url="<%= label_templates_path() %>"
data-new-label="<%= params[:new_label] || false %>"
>
<label-template-container
:label-template-url="labelTemplateUrl"
:label-templates-url="labelTemplatesUrl"
:new-label="newLabel"
/>
</div>
<%= javascript_pack_tag 'vue/label_template' %>

View file

@ -490,6 +490,24 @@ class Extends
)
STI_PRELOAD_CLASSES = %w(LinkedRepository BmtRepository)
DEFAULT_LABEL_TEMPLATE = {
zpl:
<<~HEREDOC
^XA
^MTT
^MUD,300,300
^PR2
^MD30
^LH20,20
^PW310
^CF0,23
^FO0,0^FD{{item_id}}^FS
^FO0,20^BQN,2,4^FDMA,{{item_id}}^FS
^FO95,30^FB180,4,0,L^FD{{item_name}}^FS^FS
^XZ
HEREDOC
}
end
# rubocop:enable Style/MutableConstant

View file

@ -815,6 +815,7 @@ en:
can_add_user_to_project: "Can not add user to the project."
label_templates:
new_label_template: 'New label'
index:
head_title: 'Label templates'
search_templates: 'Filter templates'
@ -837,6 +838,18 @@ en:
delete_modal:
title: 'Delete Label template(s)'
description: 'Are you sure you want to delete the selected Label template(s)?'
show:
breadcrumb_index: 'Label templates'
name_error_prefix: 'Label template name'
description_error_prefix: 'Label template description'
description_title: 'Template description'
description_placeholder: 'Enter the template description (optional)'
content_title: '%{format} template code'
preview_title: 'Template preview'
view_content_tooltip: 'Click to edit template code'
buttons:
refresh: 'Refresh preview'
save: 'Save template code'
promo:
head_title: 'Label templates'

View file

@ -45,16 +45,16 @@ Rails.application.routes.draw do
to: 'users/settings/account/addons#index',
as: 'addons'
resources :label_templates, only: %i(index new edit) do
resources :label_templates, only: %i(index show update create) do
member do
post :set_default
end
collection do
post :duplicate
post :delete
get :datatable
end
end
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

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