@@ -131,7 +131,7 @@ export default {
IconCodePlus,
IconMathFunction
},
- inject: ['t', 'save', 'template', 'withFormula'],
+ inject: ['t', 'template', 'withFormula'],
props: {
field: {
type: Object,
@@ -152,7 +152,7 @@ export default {
required: true
}
},
- emits: ['close'],
+ emits: ['close', 'save'],
data () {
return {
formula: ''
@@ -181,7 +181,7 @@ export default {
const foundField = this.fields.find((f) => f.uuid === uuid)
if (foundField) {
- return `{{${foundField.name || this.buildDefaultName(foundField, this.template.fields)}}}`
+ return `{{${foundField.name || this.buildDefaultName(foundField)}}}`
} else {
return '{{FIELD NOT FOUND}}'
}
@@ -190,7 +190,7 @@ export default {
normalizeFormula (text) {
return text.replace(/{{(.*?)}}/g, (match, name) => {
const foundField = this.fields.find((f) => {
- return (f.name || this.buildDefaultName(f, this.template.fields)).trim() === name.trim()
+ return (f.name || this.buildDefaultName(f)).trim() === name.trim()
})
if (foundField) {
@@ -212,11 +212,14 @@ export default {
} else {
this.field.preferences.formula = normalizedFormula
- if (this.field.type !== 'payment') {
+ if (this.field.type === 'payment') {
+ delete this.field.preferences.price
+ delete this.field.preferences.payment_link_id
+ } else {
this.field.readonly = !!normalizedFormula
}
- this.save()
+ this.$emit('save')
this.$emit('close')
}
diff --git a/app/javascript/template_builder/i18n.js b/app/javascript/template_builder/i18n.js
index 8f60bb5e..aea33d4b 100644
--- a/app/javascript/template_builder/i18n.js
+++ b/app/javascript/template_builder/i18n.js
@@ -1,6 +1,7 @@
const en = {
+ fixed: 'Fixed',
default: 'Default',
- save_as_custom_field: 'Save as Custom Field',
+ save_as_custom_field: 'Save as custom field',
kba: 'KBA',
analyzing_: 'Analyzing...',
download: 'Download',
@@ -31,6 +32,9 @@ const en = {
field_not_found: 'Field not found',
clear: 'Clear',
align: 'Align',
+ resize: 'Resize',
+ width: 'Width',
+ height: 'Height',
add_all_required_fields_to_continue: 'Add all required fields to continue',
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Uploaded PDF contains form fields. Keep or remove them?',
keep: 'Keep',
@@ -48,6 +52,8 @@ const en = {
type_value: 'Type value',
equal: 'Equal',
not_equal: 'Not equal',
+ greater_than: 'Greater than',
+ less_than: 'Less than',
contains: 'Contains',
does_not_contain: 'Does not contain',
not_empty: 'Not empty',
@@ -91,8 +97,9 @@ const en = {
format: 'Format',
read_only: 'Read-only',
page: 'Page',
- draw_new_area: 'Draw New Area',
- copy_to_all_pages: 'Copy to All Pages',
+ draw_new_area: 'Draw new area',
+ copy_to_all_pages: 'Copy to all pages',
+ more: 'More',
add_option: 'Add option',
option: 'Option',
options: 'Options',
@@ -173,6 +180,9 @@ const en = {
numbers_only: 'Numbers only',
letters_only: 'Letters only',
regexp_validation: 'Regexp validation',
+ custom_validation: 'Custom Validation',
+ length_validation: 'Length Validation',
+ number_range: 'Number Range',
enter_pdf_password: 'Enter PDF password',
wrong_password: 'Wrong password',
currency: 'Currency',
@@ -202,6 +212,7 @@ const en = {
}
const es = {
+ fixed: 'Fijo',
default: 'Predeterminado',
save_as_custom_field: 'Guardar como personalizado',
kba: 'KBA',
@@ -235,6 +246,9 @@ const es = {
clear: 'Borrar',
type_value: 'Escriba valor',
align: 'Alinear',
+ resize: 'Redimensionar',
+ width: 'Ancho',
+ height: 'Alto',
add_all_required_fields_to_continue: 'Agregar todos los campos requeridos para continuar',
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'El PDF cargado tiene campos. ¿Mantenerlos o eliminarlos?',
keep: 'Mantener',
@@ -252,6 +266,8 @@ const es = {
price: 'Precio',
equal: 'Igual',
not_equal: 'No es igual',
+ greater_than: 'Mayor que',
+ less_than: 'Menor que',
contains: 'Contiene',
does_not_contain: 'No contiene',
not_empty: 'No vacío',
@@ -292,6 +308,7 @@ const es = {
page: 'Página',
draw_new_area: 'Dibujar nueva área',
copy_to_all_pages: 'Copiar a todas las páginas',
+ more: 'Más',
add_option: 'Agregar opción',
option: 'Opción',
options: 'Opciones',
@@ -376,6 +393,9 @@ const es = {
numbers_only: 'Solo números',
letters_only: 'Solo letras',
regexp_validation: 'Validación de expresión regular',
+ custom_validation: 'Validación Personalizada',
+ length_validation: 'Validación de Longitud',
+ number_range: 'Rango de Números',
enter_pdf_password: 'Ingrese la contraseña del PDF',
wrong_password: 'Contraseña incorrecta',
currency: 'Moneda',
@@ -405,6 +425,7 @@ const es = {
}
const it = {
+ fixed: 'Fisso',
default: 'Predefinito',
save_as_custom_field: 'Salva come personalizzato',
kba: 'KBA',
@@ -437,6 +458,9 @@ const it = {
field_not_found: 'Campo non trovato',
clear: 'Cancella',
align: 'Allinea',
+ resize: 'Ridimensiona',
+ width: 'Larghezza',
+ height: 'Altezza',
add_all_required_fields_to_continue: 'Aggiungi tutti i campi obbligatori per continuare',
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Il PDF caricato contiene campi del modulo. Mantenerli o rimuoverli?',
keep: 'Mantieni',
@@ -454,6 +478,8 @@ const it = {
type_value: 'Inserisci valore',
equal: 'Uguale',
not_equal: 'Non uguale',
+ greater_than: 'Maggiore di',
+ less_than: 'Minore di',
contains: 'Contiene',
does_not_contain: 'Non contiene',
not_empty: 'Non vuoto',
@@ -499,6 +525,7 @@ const it = {
page: 'Pagina',
draw_new_area: 'Disegna nuova area',
copy_to_all_pages: 'Copia in tutte le pagine',
+ more: 'Altro',
add_option: 'Aggiungi opzione',
option: 'Opzione',
options: 'Opzioni',
@@ -579,6 +606,9 @@ const it = {
numbers_only: 'Solo numeri',
letters_only: 'Solo lettere',
regexp_validation: 'Validazione regexp',
+ custom_validation: 'Validazione Personalizzata',
+ length_validation: 'Validazione Lunghezza',
+ number_range: 'Intervallo Numerico',
enter_pdf_password: 'Inserisci password PDF',
wrong_password: 'Password errata',
currency: 'Valuta',
@@ -608,6 +638,7 @@ const it = {
}
const pt = {
+ fixed: 'Fixo',
default: 'Padrão',
save_as_custom_field: 'Salvar como personalizado',
kba: 'KBA',
@@ -644,6 +675,9 @@ const pt = {
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'O PDF carregado contém campos. Manter ou removê-los?',
keep: 'Manter',
align: 'Alinhar',
+ resize: 'Redimensionar',
+ width: 'Largura',
+ height: 'Altura',
left: 'Esquerda',
heading: 'Cabeçalho',
validation: 'Validação',
@@ -658,6 +692,8 @@ const pt = {
price: 'Preço',
equal: 'Igual',
not_equal: 'Não é igual',
+ greater_than: 'Maior que',
+ less_than: 'Menor que',
contains: 'Contém',
does_not_contain: 'Não contém',
not_empty: 'Não vazio',
@@ -698,6 +734,7 @@ const pt = {
page: 'Página',
draw_new_area: 'Desenhar nova área',
copy_to_all_pages: 'Copiar para todas as páginas',
+ more: 'Mais',
add_option: 'Adicionar opção',
option: 'Opção',
options: 'Opções',
@@ -782,6 +819,9 @@ const pt = {
numbers_only: 'Somente números',
letters_only: 'Somente letras',
regexp_validation: 'Validação de expressão regular',
+ custom_validation: 'Validação Personalizada',
+ length_validation: 'Validação de Comprimento',
+ number_range: 'Intervalo de Números',
enter_pdf_password: 'Digite a senha do PDF',
wrong_password: 'Senha incorreta',
currency: 'Moeda',
@@ -811,6 +851,7 @@ const pt = {
}
const fr = {
+ fixed: 'Fixe',
default: 'Par défaut',
save_as_custom_field: 'Enregistrer comme personnalisé',
kba: 'KBA',
@@ -843,6 +884,9 @@ const fr = {
field_not_found: 'Champ introuvable',
clear: 'Effacer',
align: 'Aligner',
+ resize: 'Redimensionner',
+ width: 'Largeur',
+ height: 'Hauteur',
add_all_required_fields_to_continue: 'Ajoutez tous les champs obligatoires pour continuer',
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Le PDF téléversé contient des champs de formulaire. Les conserver ou les supprimer ?',
keep: 'Conserver',
@@ -860,6 +904,8 @@ const fr = {
type_value: 'Saisir une valeur',
equal: 'Égal',
not_equal: 'Différent',
+ greater_than: 'Supérieur à',
+ less_than: 'Inférieur à',
contains: 'Contient',
does_not_contain: 'Ne contient pas',
not_empty: 'Non vide',
@@ -905,6 +951,7 @@ const fr = {
page: 'Page',
draw_new_area: 'Dessiner une zone',
copy_to_all_pages: 'Copier sur toutes les pages',
+ more: 'Plus',
add_option: 'Ajouter une option',
option: 'Option',
options: 'Options',
@@ -985,6 +1032,9 @@ const fr = {
numbers_only: 'Chiffres uniquement',
letters_only: 'Lettres uniquement',
regexp_validation: 'Validation par expression régulière',
+ custom_validation: 'Validation Personnalisée',
+ length_validation: 'Validation de Longueur',
+ number_range: 'Plage de Nombres',
enter_pdf_password: 'Saisir le mot de passe du PDF',
wrong_password: 'Mot de passe incorrect',
currency: 'Devise',
@@ -1014,6 +1064,7 @@ const fr = {
}
const de = {
+ fixed: 'Fest',
default: 'Standard',
save_as_custom_field: 'Als benutzerdefiniert speichern',
kba: 'KBA',
@@ -1046,6 +1097,9 @@ const de = {
field_not_found: 'Feld nicht gefunden',
clear: 'Leeren',
align: 'Ausrichten',
+ resize: 'Größe ändern',
+ width: 'Breite',
+ height: 'Höhe',
add_all_required_fields_to_continue: 'Fügen Sie alle erforderlichen Felder hinzu, um fortzufahren',
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Das hochgeladene PDF enthält Formularfelder. Beibehalten oder entfernen?',
keep: 'Beibehalten',
@@ -1063,6 +1117,8 @@ const de = {
type_value: 'Wert eingeben',
equal: 'Gleich',
not_equal: 'Ungleich',
+ greater_than: 'Größer als',
+ less_than: 'Kleiner als',
contains: 'Enthält',
does_not_contain: 'Enthält nicht',
not_empty: 'Nicht leer',
@@ -1108,6 +1164,7 @@ const de = {
page: 'Seite',
draw_new_area: 'Bereich zeichnen',
copy_to_all_pages: 'Auf alle Seiten kopieren',
+ more: 'Mehr',
add_option: 'Option hinzufügen',
option: 'Option',
options: 'Optionen',
@@ -1188,6 +1245,9 @@ const de = {
numbers_only: 'Nur Zahlen',
letters_only: 'Nur Buchstaben',
regexp_validation: 'RegExp-Validierung',
+ custom_validation: 'Benutzerdefinierte Validierung',
+ length_validation: 'Längenvalidierung',
+ number_range: 'Zahlenbereich',
enter_pdf_password: 'PDF-Passwort eingeben',
wrong_password: 'Falsches Passwort',
currency: 'Währung',
@@ -1217,6 +1277,7 @@ const de = {
}
const nl = {
+ fixed: 'Vast',
default: 'Standaard',
save_as_custom_field: 'Opslaan als aangepast',
kba: 'KBA',
@@ -1249,6 +1310,9 @@ const nl = {
field_not_found: 'Veld niet gevonden',
clear: 'Wissen',
align: 'Uitlijnen',
+ resize: 'Formaat wijzigen',
+ width: 'Breedte',
+ height: 'Hoogte',
add_all_required_fields_to_continue: 'Voeg alle vereiste velden toe om door te gaan',
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Geüploade PDF bevat formuliervelden. Behouden of verwijderen?',
keep: 'Behouden',
@@ -1266,6 +1330,8 @@ const nl = {
type_value: 'Typ waarde',
equal: 'Gelijk aan',
not_equal: 'Niet gelijk aan',
+ greater_than: 'Groter dan',
+ less_than: 'Kleiner dan',
contains: 'Bevat',
does_not_contain: 'Bevat niet',
not_empty: 'Niet leeg',
@@ -1311,6 +1377,7 @@ const nl = {
page: 'Pagina',
draw_new_area: 'Nieuw gebied tekenen',
copy_to_all_pages: 'Kopieer naar alle pag.',
+ more: 'Meer',
add_option: 'Optie toevoegen',
option: 'Optie',
options: 'Opties',
@@ -1391,6 +1458,9 @@ const nl = {
numbers_only: 'Alleen cijfers',
letters_only: 'Alleen letters',
regexp_validation: 'Regex validatie',
+ custom_validation: 'Aangepaste Validatie',
+ length_validation: 'Lengte Validatie',
+ number_range: 'Getalbereik',
enter_pdf_password: 'Voer PDF-wachtwoord in',
wrong_password: 'Onjuist wachtwoord',
currency: 'Valuta',
diff --git a/app/javascript/template_builder/page.vue b/app/javascript/template_builder/page.vue
index fcd60617..550658c4 100644
--- a/app/javascript/template_builder/page.vue
+++ b/app/javascript/template_builder/page.vue
@@ -29,7 +29,7 @@
:is-drag="isDrag"
@move="onSelectionBoxMove"
@contextmenu="openSelectionContextMenu"
- @close-context-menu="closeSelectionContextMenu"
+ @close-context-menu="closeContextMenu"
/>
-
-
+
import FieldArea from './area'
-import ContextMenu from './context_menu'
+import FieldContextMenu from './field_context_menu'
+import SelectionContextMenu from './selection_context_menu'
+import PageContextMenu from './page_context_menu'
import SelectionBox from './selection_box'
export default {
name: 'TemplatePage',
components: {
FieldArea,
- ContextMenu,
+ FieldContextMenu,
+ SelectionContextMenu,
+ PageContextMenu,
SelectionBox
},
- inject: ['fieldTypes', 'defaultDrawFieldType', 'fieldsDragFieldRef', 'customDragFieldRef', 'assignDropAreaSize', 'selectedAreasRef', 'template', 'isSelectModeRef'],
+ inject: ['fieldTypes', 'defaultDrawFieldType', 'fieldsDragFieldRef', 'customDragFieldRef', 'assignDropAreaSize', 'selectedAreasRef', 'template', 'isSelectModeRef', 'save'],
props: {
image: {
type: Object,
@@ -140,6 +155,11 @@ export default {
required: false,
default: null
},
+ isMobile: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
withSignatureId: {
type: Boolean,
required: false,
@@ -228,7 +248,7 @@ export default {
default: false
}
},
- emits: ['draw', 'drop-field', 'remove-area', 'copy-field', 'paste-field', 'scroll-to', 'copy-selected-areas', 'delete-selected-areas', 'align-selected-areas', 'autodetect-fields', 'add-custom-field'],
+ emits: ['draw', 'drop-field', 'remove-area', 'copy-field', 'paste-field', 'scroll-to', 'copy-selected-areas', 'delete-selected-areas', 'autodetect-fields', 'add-custom-field', 'set-draw'],
data () {
return {
areaRefs: [],
@@ -236,8 +256,7 @@ export default {
resizeDirection: null,
newAreas: [],
contextMenu: null,
- selectionRect: null,
- selectionContextMenu: null
+ selectionRect: null
}
},
computed: {
@@ -305,11 +324,6 @@ export default {
return 'text'
}
},
- isMobile () {
- const isMobileSafariIos = 'ontouchstart' in window && navigator.maxTouchPoints > 0 && /AppleWebKit/i.test(navigator.userAgent)
-
- return isMobileSafariIos || /android|iphone|ipad/i.test(navigator.userAgent)
- },
resizeDirectionClasses () {
return {
nwse: 'cursor-nwse-resize',
@@ -399,29 +413,32 @@ export default {
}
},
openSelectionContextMenu (event) {
+ if (!this.editable) {
+ return
+ }
+
+ event.preventDefault()
+ event.stopPropagation()
+
const rect = this.$el.getBoundingClientRect()
- this.selectionContextMenu = {
+ this.contextMenu = {
x: event.clientX,
y: event.clientY,
relativeX: (event.clientX - rect.left) / rect.width,
- relativeY: (event.clientY - rect.top) / rect.height
+ relativeY: (event.clientY - rect.top) / rect.height,
+ areas: this.selectedAreasRef.value
}
},
- closeSelectionContextMenu () {
- this.selectionContextMenu = null
- },
handleSelectionCopy () {
this.$emit('copy-selected-areas')
- this.closeSelectionContextMenu()
+
+ this.closeContextMenu()
},
handleSelectionDelete () {
this.$emit('delete-selected-areas')
- this.closeSelectionContextMenu()
- },
- handleSelectionAlign (direction) {
- this.$emit('align-selected-areas', direction)
- this.closeSelectionContextMenu()
+
+ this.closeContextMenu()
},
closeContextMenu () {
this.contextMenu = null
diff --git a/app/javascript/template_builder/page_context_menu.vue b/app/javascript/template_builder/page_context_menu.vue
new file mode 100644
index 00000000..3cec3c65
--- /dev/null
+++ b/app/javascript/template_builder/page_context_menu.vue
@@ -0,0 +1,159 @@
+
+
+
+
+
+ {{ t('paste') }}
+
+ {{ isMac ? '⌘V' : 'Ctrl+V' }}
+
+
+
+
+
+ {{ isSelectModeRef.value ? t('draw_fields') : t('select_fields') }}
+
+ Tab
+
+
+
+
+ {{ t('autodetect_fields') }}
+
+
+
+
+
diff --git a/app/javascript/template_builder/preview.vue b/app/javascript/template_builder/preview.vue
index ff7864fd..025ad692 100644
--- a/app/javascript/template_builder/preview.vue
+++ b/app/javascript/template_builder/preview.vue
@@ -119,6 +119,7 @@
@@ -145,7 +146,7 @@ export default {
GoogleDriveDocumentSettings,
IconSortDescending2
},
- inject: ['t'],
+ inject: ['t', 'getFieldTypeIndex'],
props: {
item: {
type: Object,
diff --git a/app/javascript/template_builder/selection_context_menu.vue b/app/javascript/template_builder/selection_context_menu.vue
new file mode 100644
index 00000000..4649e011
--- /dev/null
+++ b/app/javascript/template_builder/selection_context_menu.vue
@@ -0,0 +1,348 @@
+
+
+
+
+
+
+ {{ t('align_left') }}
+
+
+
+ {{ t('align_right') }}
+
+
+
+ {{ t('align_top') }}
+
+
+
+ {{ t('align_bottom') }}
+
+
+
+
+
+ {{ t('width') }}
+
+
+
+ {{ t('height') }}
+
+
+
+
+
+ {{ t('font') }}
+
+
+
+ {{ t('condition') }}
+
+
+
+
+
+ {{ t('copy') }}
+
+ {{ isMac ? '⌘C' : 'Ctrl+C' }}
+
+
+
+
+ {{ t('remove') }}
+
+ Del
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/template_builder/upload.vue b/app/javascript/template_builder/upload.vue
index 5f4faa57..813ce12e 100644
--- a/app/javascript/template_builder/upload.vue
+++ b/app/javascript/template_builder/upload.vue
@@ -290,7 +290,11 @@ export default {
if (resp.ok) {
resp.json().then((data) => {
this.$emit('success', data)
- this.$refs.input.value = ''
+
+ if (this.$refs.input) {
+ this.$refs.input.value = ''
+ }
+
this.isLoading = false
})
} else if (resp.status === 422) {
diff --git a/app/jobs/process_submitter_completion_job.rb b/app/jobs/process_submitter_completion_job.rb
index 0976b3bf..135c0c38 100644
--- a/app/jobs/process_submitter_completion_job.rb
+++ b/app/jobs/process_submitter_completion_job.rb
@@ -164,11 +164,13 @@ class ProcessSubmitterCompletionJob
next_submitter_items =
if submission.template_submitters.any? { |s| s['order'] }
submitter_groups =
- submission.template_submitters.group_by.with_index { |s, index| s['order'] || index }
+ submission.template_submitters
+ .group_by.with_index { |s, index| s['order'] || index }
+ .sort_by(&:first).pluck(1)
- current_group_index = submitter_groups.find { |_, group| group.any? { |s| s['uuid'] == submitter.uuid } }&.first
+ current_group_index = submitter_groups.index { |group| group.any? { |s| s['uuid'] == submitter.uuid } }
- if submitter_groups[current_group_index + 1] &&
+ if current_group_index && submitter_groups[current_group_index + 1] &&
submitters_index.values_at(*submitter_groups[current_group_index].pluck('uuid'))
.compact.all?(&:completed_at?)
submitter_groups[current_group_index + 1]
diff --git a/app/views/email_smtp_settings/index.html.erb b/app/views/email_smtp_settings/index.html.erb
index 76733144..7e556a47 100644
--- a/app/views/email_smtp_settings/index.html.erb
+++ b/app/views/email_smtp_settings/index.html.erb
@@ -39,7 +39,7 @@