diff --git a/Gemfile.lock b/Gemfile.lock index 7ad54e84..962c2364 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -205,7 +205,7 @@ GEM railties (>= 6.1.0) faker (3.6.0) i18n (>= 1.8.11, < 2) - faraday (2.14.0) + faraday (2.14.1) faraday-net_http (>= 2.0, < 3.5) json logger @@ -285,7 +285,7 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.18.0) + json (2.18.1) jwt (3.1.2) base64 language_server-protocol (3.17.0.5) diff --git a/README.md b/README.md index 9753da2a..fc1273d5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

- DocuSeal + DocuSeal
DocuSeal diff --git a/app/javascript/elements/toggle_cookies.js b/app/javascript/elements/toggle_cookies.js index 8d030cb3..8f59ea6c 100644 --- a/app/javascript/elements/toggle_cookies.js +++ b/app/javascript/elements/toggle_cookies.js @@ -12,6 +12,6 @@ export default class extends HTMLElement { } get button () { - return this.querySelector('button') + return this.querySelector('button, label') } } diff --git a/app/javascript/submission_form/date_step.vue b/app/javascript/submission_form/date_step.vue index 08319dd8..5645b8b0 100644 --- a/app/javascript/submission_form/date_step.vue +++ b/app/javascript/submission_form/date_step.vue @@ -42,7 +42,7 @@
-
+
f.required))) { indexesList.push(requiredEmptyStepIndex) } - if (lastFilledStepIndex !== -1) { + if (lastFilledStepIndex !== -1 && (!this.onlyRequiredFields || this.stepFields[lastFilledStepIndex].some((f) => f.required))) { indexesList.push(lastFilledStepIndex) } @@ -1239,11 +1255,11 @@ export default { } }, checkFieldConditions (field, cache = {}) { - if (cache[field.uuid] !== undefined) { - return cache[field.uuid] - } + const cacheKey = field.uuid || field.attachment_uuid - cache[field.uuid] = true + if (cache[cacheKey] !== undefined) { + return cache[cacheKey] + } if (field.conditions?.length) { const result = field.conditions.reduce((acc, cond) => { @@ -1256,10 +1272,12 @@ export default { return acc }, []) - cache[field.uuid] = !result.includes(false) + cache[cacheKey] = !result.includes(false) + } else { + cache[cacheKey] = true } - return cache[field.uuid] + return cache[cacheKey] }, checkFieldCondition (condition, cache = {}) { const field = this.fieldsUuidIndex[condition.field_uuid] @@ -1329,6 +1347,13 @@ export default { return `${this.t('option')} ${index + 1}` } }, + findNextStep (currentStepIndex) { + if (this.onlyRequiredFields) { + return this.stepFields.find((step, index) => index > currentStepIndex && step.some((f) => f.required)) + } else { + return this.stepFields[currentStepIndex + 1] + } + }, maybeTrackEmailClick () { const { queryParams } = this @@ -1475,7 +1500,7 @@ export default { this.isSubmittingComplete = true } - const submitStep = this.currentStep + const submitStepIndex = this.currentStep const stepPromise = ['signature', 'phone', 'initials', 'payment', 'verification', 'kba'].includes(this.currentField.type) ? this.$refs.currentStep.submit @@ -1483,7 +1508,7 @@ export default { stepPromise().then(async () => { const emptyRequiredField = this.stepFields.find((fields, index) => { - if (forceComplete ? index === submitStep : index >= submitStep) { + if (forceComplete ? index === submitStepIndex : index >= submitStepIndex) { return false } @@ -1493,7 +1518,7 @@ export default { }) const formData = new FormData(this.$refs.form) - const isLastStep = (submitStep === this.stepFields.length - 1) || forceComplete + const isLastStep = (this.onlyRequiredFields ? !this.findNextStep(submitStepIndex) : (submitStepIndex === this.stepFields.length - 1)) || forceComplete if (isLastStep && !emptyRequiredField && !this.inviteSubmitters.length && !this.optionalInviteSubmitters.length) { formData.append('completed', 'true') @@ -1537,7 +1562,7 @@ export default { return Promise.reject(new Error(data.error)) } - const nextStep = (isLastStep && emptyRequiredField) || (forceComplete ? null : this.stepFields[submitStep + 1]) + const nextStep = (isLastStep && emptyRequiredField) || (forceComplete ? null : this.findNextStep(submitStepIndex)) if (nextStep) { if (this.alwaysMinimize) { diff --git a/app/javascript/submission_form/initials_step.vue b/app/javascript/submission_form/initials_step.vue index 2dede0e4..8e1cc1b5 100644 --- a/app/javascript/submission_form/initials_step.vue +++ b/app/javascript/submission_form/initials_step.vue @@ -175,6 +175,10 @@ export default { type: Object, required: true }, + submitter: { + type: Object, + required: true + }, dryRun: { type: Boolean, required: false, @@ -257,6 +261,14 @@ export default { this.$refs.canvas.getContext('2d').scale(scale, scale) + if (!this.isDrawInitials) { + this.$nextTick(() => { + if (this.$refs.textInput) { + this.initTextInitial() + } + }) + } + this.intersectionObserver?.disconnect() } }) @@ -332,12 +344,27 @@ export default { if (!this.isDrawInitials) { this.$nextTick(() => { - this.$refs.textInput.focus() + if (this.$refs.textInput) { + if (!this.submitter.name) { + this.$refs.textInput.focus() + } - this.$emit('start') + this.initTextInitial() + + this.$emit('start') + } }) } }, + initTextInitial () { + if (this.submitter.name) { + this.$refs.textInput.value = this.submitter.name.trim().split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase() || '').join('') + } + + if (this.$refs.textInput.value) { + this.updateWrittenInitials({ target: this.$refs.textInput }) + } + }, async submit () { if (this.modelValue || this.computedPreviousValue) { if (this.computedPreviousValue) { diff --git a/app/javascript/submission_form/signature_step.vue b/app/javascript/submission_form/signature_step.vue index 91937ec9..ef5ba45c 100644 --- a/app/javascript/submission_form/signature_step.vue +++ b/app/javascript/submission_form/signature_step.vue @@ -316,7 +316,7 @@ import FileDropzone from './dropzone' import MarkdownContent from './markdown_content' import { v4 } from 'uuid' -let isFontLoaded = false +let fontLoadPromise = null const scale = 3 @@ -482,6 +482,14 @@ export default { if (entry.isIntersecting) { this.setCanvasSize() + if (this.isTextSignature) { + this.$nextTick(() => { + if (this.$refs.textInput) { + this.initTypedSignature() + } + }) + } + this.intersectionObserver?.disconnect() } }) @@ -504,6 +512,10 @@ export default { }) this.resizeObserver.observe(this.$refs.canvas.parentNode) + + if (this.isTextSignature) { + this.loadFont() + } } }, beforeUnmount () { @@ -541,7 +553,7 @@ export default { this.pad.fromData(scaledData) } else if (this.isTextSignature && this.$refs.textInput) { - this.updateWrittenSignature({ target: { value: this.$refs.textInput.value } }) + this.updateWrittenSignature({ target: this.$refs.textInput }) } }, remove () { @@ -551,17 +563,17 @@ export default { this.isSignatureStarted = false }, loadFont () { - if (!isFontLoaded) { + if (!fontLoadPromise) { const font = new FontFace('Dancing Script', `url(${this.baseUrl}/fonts/DancingScript-Regular.otf) format("opentype")`) - font.load().then((loadedFont) => { + fontLoadPromise = font.load().then((loadedFont) => { document.fonts.add(loadedFont) - - isFontLoaded = true }).catch((error) => { console.error('Font loading failed:', error) }) } + + return fontLoadPromise }, showQr () { this.isShowQr = true @@ -661,14 +673,29 @@ export default { if (this.isTextSignature) { this.$nextTick(() => { - this.$refs.textInput.focus() + if (this.$refs.textInput) { + if (!this.submitter.name) { + this.$refs.textInput.focus() + } - this.loadFont() + this.initTypedSignature() - this.$emit('start') + this.$emit('start') + } }) } }, + async initTypedSignature () { + if (this.submitter.name) { + this.$refs.textInput.value = this.submitter.name + } + + await this.loadFont() + + if (this.$refs.textInput.value) { + this.updateWrittenSignature({ target: this.$refs.textInput }) + } + }, drawImage (event) { this.remove() this.clear() diff --git a/app/javascript/template_builder/logo.vue b/app/javascript/template_builder/logo.vue index 31621d60..8f878e14 100644 --- a/app/javascript/template_builder/logo.vue +++ b/app/javascript/template_builder/logo.vue @@ -2,87 +2,20 @@ - - - - - - - - - - - - - - - diff --git a/app/views/shared/_logo.html.erb b/app/views/shared/_logo.html.erb index d97d9913..ce505b8f 100644 --- a/app/views/shared/_logo.html.erb +++ b/app/views/shared/_logo.html.erb @@ -1,32 +1,4 @@ - - - - - - - - - - - - - - - - - - + + + diff --git a/app/views/submissions/new.html.erb b/app/views/submissions/new.html.erb index 46088dbb..92073812 100644 --- a/app/views/submissions/new.html.erb +++ b/app/views/submissions/new.html.erb @@ -1,6 +1,7 @@ <% require_phone_2fa = @template.preferences['require_phone_2fa'] == true %> <% require_email_2fa = @template.preferences['require_email_2fa'] == true %> <% prefillable_fields = @template.fields.select { |f| f['prefillable'] } %> +<% default_tab = cookies.permanent[:add_recipients_tab].presence || 'email' %> <% recipient_form_fields = Accounts.load_recipient_form_fields(current_account) if prefillable_fields.blank? %> <% only_detailed = require_phone_2fa || require_email_2fa || prefillable_fields.present? || recipient_form_fields.present? %> <%= render 'shared/turbo_modal_large', title: params[:selfsign] ? t('add_recipients') : t('add_new_recipients') do %> @@ -8,25 +9,25 @@
<% options.each_with_index do |(label, value), index| %> -
- <%= radio_button_tag 'option', value, value == (only_detailed ? 'detailed' : 'email'), class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> + <%= content_tag(value == 'list' ? 'span' : 'toggle-cookies', data: { value:, key: 'add_recipients_tab' }) do %> + <%= radio_button_tag 'option', value, value == (only_detailed ? 'detailed' : default_tab), class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> -
+ <% end %> <% end %>
<% unless only_detailed %> -
+
<%= render 'email_form', template: @template %>
-