mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-11-10 17:36:33 +08:00
Merge branch 'features/label-templates' into ai-sci-7054-add-flyout-to-search-repository-columns-to-zpl-editor
This commit is contained in:
commit
6d8908c1f5
12 changed files with 257 additions and 8 deletions
1
Gemfile
1
Gemfile
|
@ -89,6 +89,7 @@ gem 'underscore-rails'
|
|||
gem 'wicked_pdf'
|
||||
gem 'wkhtmltopdf-heroku', '2.12.5'
|
||||
|
||||
gem 'aws-sdk-lambda'
|
||||
gem 'aws-sdk-rails'
|
||||
gem 'aws-sdk-s3'
|
||||
gem 'delayed_job_active_record'
|
||||
|
|
|
@ -137,6 +137,9 @@ GEM
|
|||
aws-sdk-kms (1.41.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-lambda (1.57.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-rails (3.6.0)
|
||||
aws-record (~> 2)
|
||||
aws-sdk-ses (~> 1)
|
||||
|
@ -634,6 +637,7 @@ DEPENDENCIES
|
|||
auto_strip_attributes (~> 2.1)
|
||||
autosize-rails
|
||||
awesome_print
|
||||
aws-sdk-lambda
|
||||
aws-sdk-rails
|
||||
aws-sdk-s3
|
||||
base62
|
||||
|
|
57
app/assets/stylesheets/label_templates/label_preview.scss
Normal file
57
app/assets/stylesheets/label_templates/label_preview.scss
Normal file
|
@ -0,0 +1,57 @@
|
|||
.label-preview {
|
||||
margin-top: 16px;
|
||||
max-width: 410px;
|
||||
}
|
||||
|
||||
.label-preview__header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.label-preview__options-button {
|
||||
color: $brand-primary-light;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.label-preview__image {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, .2);
|
||||
margin-top: 16px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.label-preview__controls {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&.open {
|
||||
height: auto;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.label-preview__controls__size {
|
||||
display: flex;
|
||||
margin-top: 16px;
|
||||
|
||||
.sci-input-container {
|
||||
margin-right: 8px;
|
||||
|
||||
&:last-child {
|
||||
flex-basis: 50%;
|
||||
flex-shrink: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-preview__refresh {
|
||||
cursor: pointer;
|
||||
margin: 16px auto;
|
||||
max-width: 160px;
|
||||
text-align: center;
|
||||
}
|
|
@ -4,7 +4,7 @@ class LabelTemplatesController < ApplicationController
|
|||
include InputSanitizeHelper
|
||||
|
||||
before_action :check_feature_enabled
|
||||
before_action :check_view_permissions, only: %i(index datatable)
|
||||
before_action :check_view_permissions, except: %i(create duplicate set_default delete update)
|
||||
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(show set_default update)
|
||||
|
@ -89,6 +89,17 @@ class LabelTemplatesController < ApplicationController
|
|||
render json: LabelTemplates::TagService.new(current_team).tags
|
||||
end
|
||||
|
||||
def zpl_preview
|
||||
service = LabelTemplatesPreviewService.new(params, current_user)
|
||||
payload = service.generate_zpl_preview!
|
||||
|
||||
if service.error.blank?
|
||||
render json: { base64_preview: payload }
|
||||
else
|
||||
render json: { error: I18n.t('errors.general') }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_feature_enabled
|
||||
|
|
|
@ -16,6 +16,7 @@ window.initLabelTemplateComponent = () => {
|
|||
return {
|
||||
labelTemplateUrl: $('#labelTemplateContainer').data('label-template-url'),
|
||||
labelTemplatesUrl: $('#labelTemplateContainer').data('label-templates-url'),
|
||||
previewUrl: $('#labelTemplateContainer').data('preview-url'),
|
||||
newLabel: $('#labelTemplateContainer').data('new-label')
|
||||
};
|
||||
}
|
||||
|
|
128
app/javascript/vue/label_template/components/label_preview.vue
Normal file
128
app/javascript/vue/label_template/components/label_preview.vue
Normal file
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<div ref="labelPreview" class="label-preview">
|
||||
<div class="label-preview__header">
|
||||
<div class="title">
|
||||
{{ i18n.t('label_templates.show.preview_title') }}
|
||||
</div>
|
||||
<div class="label-preview__options-button" @click="optionsOpen = !optionsOpen">
|
||||
{{ i18n.t('label_templates.label_preview.options') }}
|
||||
|
||||
<i class="fas" :class="{ 'fa-angle-down': !optionsOpen, 'fa-angle-up': optionsOpen }"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="label-preview__controls" :class="{'open': optionsOpen}">
|
||||
<div class="label-preview__controls__units">
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('label_templates.label_preview.units') }}</label>
|
||||
<DropdownSelector
|
||||
:disableSearch="true"
|
||||
:options="[{ value: 'in', label: i18n.t(`label_templates.label_preview.in`) }, { value: 'mm', label: i18n.t(`label_templates.label_preview.mm`) }]"
|
||||
:selectorId="'UnitSelector'"
|
||||
:selectedValue="unit"
|
||||
@dropdown:changed="updateUnit" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="label-preview__controls__size">
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('label_templates.label_preview.height') }}</label>
|
||||
<input v-model="height" type="text" class="sci-input-field" />
|
||||
</div>
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('label_templates.label_preview.width') }}</label>
|
||||
<input v-model="width" type="text" class="sci-input-field" />
|
||||
</div>
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('label_templates.label_preview.density') }}</label>
|
||||
<DropdownSelector
|
||||
:key="unit"
|
||||
:disableSearch="true"
|
||||
:options="unit === 'in' ? DPI_RESOLUTION_OPTIONS : DPMM_RESOLUTION_OPTIONS"
|
||||
:selectorId="'DensitySelector'"
|
||||
:selectedValue="density"
|
||||
@dropdown:changed="updateDensity" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="label-preview__refresh" @click="refreshPreview">
|
||||
<i class="fas fa-sync"></i>
|
||||
{{ i18n.t('label_templates.label_preview.refresh_preview') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="base64Image" class="label-preview__image">
|
||||
<img :src="`data:image/png;base64,${base64Image}`" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const DPI_RESOLUTION_OPTIONS = [
|
||||
{ value: 6, label: '6 dpi' },
|
||||
{ value: 8, label: '8 dpi', selected: true },
|
||||
{ value: 12, label: '12 dpi'},
|
||||
{ value: 24, label: '24 dpi' }
|
||||
]
|
||||
|
||||
const DPMM_RESOLUTION_OPTIONS = [
|
||||
{ value: 6, label: '152 dpmm (6 dpi)' },
|
||||
{ value: 8, label: '203 dpmm (8 dpi)', selected: true },
|
||||
{ value: 12, label: '300 dpmm (12 dpi)' },
|
||||
{ value: 24, label: '600 dpmm (24 dpi)' }
|
||||
]
|
||||
|
||||
import DropdownSelector from 'vue/shared/dropdown_selector.vue'
|
||||
|
||||
export default {
|
||||
name: 'LabelPreview',
|
||||
components: { DropdownSelector },
|
||||
props: {
|
||||
zpl: { type: String, required: true },
|
||||
previewUrl: { type: String, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
DPMM_RESOLUTION_OPTIONS,
|
||||
DPI_RESOLUTION_OPTIONS,
|
||||
optionsOpen: false,
|
||||
width: null,
|
||||
height: null,
|
||||
unit: 'in',
|
||||
density: 8,
|
||||
base64Image: null,
|
||||
imageStyle: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.refreshPreview();
|
||||
},
|
||||
computed: {
|
||||
widthMm() {
|
||||
return this.unit === 'in' ? this.width * 25.4 : this.width;
|
||||
},
|
||||
heightMm() {
|
||||
return this.unit === 'in' ? this.height * 25.4 : this.height;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshPreview() {
|
||||
$.ajax({
|
||||
url: this.previewUrl,
|
||||
type: 'GET',
|
||||
data: {
|
||||
zpl: this.zpl,
|
||||
width: this.widthMm,
|
||||
height: this.heightMm,
|
||||
density: this.density
|
||||
},
|
||||
success: (result) => {
|
||||
this.base64Image = result.base64_preview;
|
||||
}
|
||||
});
|
||||
},
|
||||
updateUnit(unit) {
|
||||
this.unit = unit;
|
||||
},
|
||||
updateDensity(density) {
|
||||
this.density = density;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -81,9 +81,7 @@
|
|||
</div>
|
||||
|
||||
<div class="label-preview-container">
|
||||
<div class="title">
|
||||
{{ i18n.t('label_templates.show.preview_title') }}
|
||||
</div>
|
||||
<LabelPreview :zpl='labelTemplate.attributes.content' :previewUrl="previewUrl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -94,12 +92,14 @@
|
|||
|
||||
import InlineEdit from 'vue/shared/inline_edit.vue'
|
||||
import InsertFieldDropdown from 'vue/label_template/insert_field_dropdown.vue'
|
||||
import LabelPreview from './components/label_preview.vue'
|
||||
|
||||
export default {
|
||||
name: 'LabelTemplateContainer',
|
||||
props: {
|
||||
labelTemplateUrl: String,
|
||||
labelTemplatesUrl: String,
|
||||
previewUrl: String,
|
||||
newLabel: Boolean
|
||||
},
|
||||
data() {
|
||||
|
@ -114,7 +114,7 @@
|
|||
cursorPos: 0
|
||||
}
|
||||
},
|
||||
components: {InlineEdit, InsertFieldDropdown},
|
||||
components: {InlineEdit, InsertFieldDropdown, LabelPreview},
|
||||
created() {
|
||||
$.get(this.labelTemplateUrl, (result) => {
|
||||
this.labelTemplate = result.data
|
||||
|
|
33
app/services/label_templates_preview_service.rb
Normal file
33
app/services/label_templates_preview_service.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class LabelTemplatesPreviewService
|
||||
extend Service
|
||||
|
||||
attr_reader :error, :preview
|
||||
|
||||
def initialize(params, user)
|
||||
@user = user
|
||||
@params = params
|
||||
end
|
||||
|
||||
def generate_zpl_preview!
|
||||
client = Aws::Lambda::Client.new(region: ENV['AWS_REGION'])
|
||||
resp = client.invoke(
|
||||
function_name: 'BinaryKitsZplViewer',
|
||||
invocation_type: 'RequestResponse',
|
||||
log_type: 'Tail',
|
||||
payload:
|
||||
"{ \"content\": #{@params[:zpl].to_json},"\
|
||||
"\"width\": #{@params[:width]},"\
|
||||
"\"height\": #{@params[:height]},"\
|
||||
"\"density\": #{@params[:density]} "\
|
||||
"}"
|
||||
)
|
||||
|
||||
if resp.function_error.nil?
|
||||
@preview = resp.payload.string.delete('"')
|
||||
else
|
||||
@error = resp.function_error.string
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,12 +6,14 @@
|
|||
|
||||
<div id="labelTemplateContainer"
|
||||
data-label-template-url="<%= label_template_path(params[:id], format: :json) %>"
|
||||
data-label-templates-url="<%= label_templates_path() %>"
|
||||
data-label-templates-url="<%= label_templates_path %>"
|
||||
data-preview-url="<%= zpl_preview_label_templates_path %>"
|
||||
data-new-label="<%= params[:new_label] || false %>"
|
||||
>
|
||||
<label-template-container
|
||||
:label-template-url="labelTemplateUrl"
|
||||
:label-templates-url="labelTemplatesUrl"
|
||||
:preview-url="previewUrl"
|
||||
:new-label="newLabel"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -872,7 +872,16 @@ en:
|
|||
name: 'Name'
|
||||
added_on: 'Added On'
|
||||
added_by: 'Added by'
|
||||
|
||||
label_preview:
|
||||
title: 'Preview'
|
||||
options: 'Options'
|
||||
units: 'Measurement units'
|
||||
height: 'Height'
|
||||
width: 'Width'
|
||||
density: 'Density'
|
||||
refresh_preview: 'Refresh preview'
|
||||
mm: 'Millimeters (mm) 1 cm = 10 mm'
|
||||
in: 'Inches (in)'
|
||||
promo:
|
||||
head_title: 'Label templates'
|
||||
promo_title: 'Label all your tubes & containers by your own standards'
|
||||
|
|
|
@ -54,6 +54,7 @@ Rails.application.routes.draw do
|
|||
post :delete
|
||||
get :datatable
|
||||
get :template_tags
|
||||
get :zpl_preview
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,11 +17,13 @@ class AddColumnsToLabelTemplates < ActiveRecord::Migration[6.1]
|
|||
add_reference :label_templates, :team, index: true, foreign_key: true
|
||||
|
||||
if Team.count.positive?
|
||||
LabelTemplate.first.update(
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
LabelTemplate.first.update_columns(
|
||||
created_by_id: Team.first.created_by_id,
|
||||
last_modified_by_id: Team.first.created_by_id,
|
||||
team_id: Team.first.id
|
||||
)
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue