From 377244f9489e942cc990a1fcc151f6d7eaef6aef Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 2 Jul 2024 20:27:25 +0300 Subject: [PATCH] add form preview --- .../templates_form_preview_controller.rb | 38 ++++++++++ app/javascript/form.js | 3 + app/javascript/submission_form/area.vue | 8 ++- app/javascript/submission_form/areas.vue | 6 ++ .../submission_form/attachment_step.vue | 6 ++ app/javascript/submission_form/dropzone.vue | 45 ++++++++---- app/javascript/submission_form/form.vue | 38 +++++++--- app/javascript/submission_form/image_step.vue | 6 ++ .../submission_form/initials_step.vue | 44 ++++++++---- .../submission_form/signature_step.vue | 48 +++++++++---- app/javascript/template_builder/builder.vue | 69 ++++++++++++++----- app/javascript/template_builder/fields.vue | 2 +- app/views/submit_form/_banner.html.erb | 2 +- .../submit_form/_submission_form.html.erb | 2 +- app/views/submit_form/show.html.erb | 4 +- .../templates_form_preview/show.html.erb | 24 +++++++ config/routes.rb | 1 + 17 files changed, 273 insertions(+), 73 deletions(-) create mode 100644 app/controllers/templates_form_preview_controller.rb create mode 100644 app/views/templates_form_preview/show.html.erb diff --git a/app/controllers/templates_form_preview_controller.rb b/app/controllers/templates_form_preview_controller.rb new file mode 100644 index 00000000..fb633560 --- /dev/null +++ b/app/controllers/templates_form_preview_controller.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class TemplatesFormPreviewController < ApplicationController + PRELOAD_ALL_PAGES_AMOUNT = 200 + + layout 'form' + + load_and_authorize_resource :template + + def show + @submitter = Submitter.new(uuid: params[:uuid] || @template.submitters.first['uuid'], + account: current_account, + submission: @template.submissions.new(template_submitters: @template.submitters, + account: current_account)) + + @submitter.submission.submitters = @template.submitters.map { |item| Submitter.new(uuid: item['uuid']) } + + ActiveRecord::Associations::Preloader.new( + records: [@submitter], + associations: [submission: [:template, { template_schema_documents: :blob }]] + ).call + + total_pages = + @submitter.submission.template_schema_documents.sum { |e| e.metadata.dig('pdf', 'number_of_pages').to_i } + + if total_pages < PRELOAD_ALL_PAGES_AMOUNT + ActiveRecord::Associations::Preloader.new( + records: @submitter.submission.template_schema_documents, + associations: [:blob, { preview_images_attachments: :blob }] + ).call + end + + @attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments) + .preload(:blob).index_by(&:uuid) + + @form_configs = Submitters::FormConfigs.call(@submitter) + end +end diff --git a/app/javascript/form.js b/app/javascript/form.js index 4d4d62a1..be9cc256 100644 --- a/app/javascript/form.js +++ b/app/javascript/form.js @@ -19,6 +19,9 @@ safeRegisterElement('submission-form', class extends HTMLElement { goToLast: this.dataset.goToLast === 'true', isDemo: this.dataset.isDemo === 'true', attribution: this.dataset.attribution !== 'false', + scrollPadding: this.dataset.scrollPadding || '-80px', + dryRun: this.dataset.dryRun === 'true', + expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null, withConfetti: this.dataset.withConfetti !== 'false', withDisclosure: this.dataset.withDisclosure === 'true', withTypedSignature: this.dataset.withTypedSignature !== 'false', diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index d15d3a29..3bd800ce 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -41,7 +41,8 @@
({}) }, + scrollPadding: { + type: String, + required: false, + default: '-80px' + }, attachmentsIndex: { type: Object, required: false, diff --git a/app/javascript/submission_form/attachment_step.vue b/app/javascript/submission_form/attachment_step.vue index 284d0c44..f64dbb23 100644 --- a/app/javascript/submission_form/attachment_step.vue +++ b/app/javascript/submission_form/attachment_step.vue @@ -52,6 +52,7 @@ :message="`${t('upload')} ${field.name || t('files')}${field.required ? '' : ` (${t('optional')})`}`" :submitter-slug="submitterSlug" :multiple="true" + :dry-run="dryRun" @upload="onUpload" />
@@ -80,6 +81,11 @@ export default { type: String, required: true }, + dryRun: { + type: Boolean, + required: false, + default: false + }, attachmentsIndex: { type: Object, required: false, diff --git a/app/javascript/submission_form/dropzone.vue b/app/javascript/submission_form/dropzone.vue index 922aac67..89126c67 100644 --- a/app/javascript/submission_form/dropzone.vue +++ b/app/javascript/submission_form/dropzone.vue @@ -65,6 +65,11 @@ export default { type: String, required: true }, + dryRun: { + type: Boolean, + required: false, + default: false + }, accept: { type: String, required: false, @@ -107,20 +112,36 @@ export default { Array.from(files).map(async (file) => { const formData = new FormData() - if (file.type === 'image/bmp') { - file = await this.convertBmpToPng(file) - } + if (this.dryRun) { + return new Promise((resolve) => { + const reader = new FileReader() + + reader.readAsDataURL(file) - formData.append('file', file) - formData.append('submitter_slug', this.submitterSlug) - formData.append('name', 'attachments') + reader.onloadend = () => { + resolve({ + url: reader.result, + uuid: Math.random().toString(), + filename: file.name + }) + } + }) + } else { + if (file.type === 'image/bmp') { + file = await this.convertBmpToPng(file) + } - return fetch(this.baseUrl + '/api/attachments', { - method: 'POST', - body: formData - }).then(resp => resp.json()).then((data) => { - return data - }) + formData.append('file', file) + formData.append('submitter_slug', this.submitterSlug) + formData.append('name', 'attachments') + + return fetch(this.baseUrl + '/api/attachments', { + method: 'POST', + body: formData + }).then(resp => resp.json()).then((data) => { + return data + }) + } })).then((result) => { this.$emit('upload', result) }).finally(() => { diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index ac3ae7a4..724f34b8 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -6,7 +6,8 @@ :attachments-index="attachmentsIndex" :with-label="!isAnonymousChecboxes && showFieldNames" :current-step="currentStepFields" - @focus-step="[saveStep(), goToStep($event, false, true), currentField.type !== 'checkbox' ? isFormVisible = true : '']" + :scroll-padding="scrollPadding" + @focus-step="[saveStep(), currentField.type !== 'checkbox' ? isFormVisible = true : '', goToStep($event, false, true)]" /> { this.recalculateButtonDisabledKey = Math.random() - Promise.all([ - this.maybeTrackEmailClick(), - this.maybeTrackSmsClick() - ]).finally(() => { - this.trackViewForm() - }) + if (!this.dryRun) { + Promise.all([ + this.maybeTrackEmailClick(), + this.maybeTrackSmsClick() + ]).finally(() => { + this.trackViewForm() + }) + } }) }, methods: { @@ -1009,7 +1021,13 @@ export default { const currentFieldUuids = this.currentStepFields.map((f) => f.uuid) const currentFieldType = this.currentField.type - if (this.isCompleted) { + if (this.dryRun) { + currentFieldUuids.forEach((fieldUuid) => { + this.submittedValues[fieldUuid] = this.values[fieldUuid] + }) + + return Promise.resolve({}) + } else if (this.isCompleted) { return Promise.resolve({}) } else { return fetch(this.baseUrl + this.submitPath, { @@ -1059,7 +1077,7 @@ export default { const formData = new FormData(this.$refs.form) const isLastStep = this.currentStep === this.stepFields.length - 1 - if (isLastStep && !emptyRequiredField && !this.dryRun) { + if (isLastStep && !emptyRequiredField) { formData.append('completed', 'true') } diff --git a/app/javascript/submission_form/image_step.vue b/app/javascript/submission_form/image_step.vue index d68e7cf0..4499eb83 100644 --- a/app/javascript/submission_form/image_step.vue +++ b/app/javascript/submission_form/image_step.vue @@ -37,6 +37,7 @@ @@ -66,6 +67,11 @@ export default { required: false, default: true }, + dryRun: { + type: Boolean, + required: false, + default: false + }, submitterSlug: { type: String, required: true diff --git a/app/javascript/submission_form/initials_step.vue b/app/javascript/submission_form/initials_step.vue index 9e748817..fdb51373 100644 --- a/app/javascript/submission_form/initials_step.vue +++ b/app/javascript/submission_form/initials_step.vue @@ -143,6 +143,11 @@ export default { type: Object, required: true }, + dryRun: { + type: Boolean, + required: false, + default: false + }, submitterSlug: { type: String, required: true @@ -282,21 +287,36 @@ export default { cropCanvasAndExportToPNG(this.$refs.canvas).then(async (blob) => { const file = new File([blob], 'initials.png', { type: 'image/png' }) - const formData = new FormData() + if (this.dryRun) { + const reader = new FileReader() + + reader.readAsDataURL(file) - formData.append('file', file) - formData.append('submitter_slug', this.submitterSlug) - formData.append('name', 'attachments') + reader.onloadend = () => { + const attachment = { url: reader.result, uuid: Math.random().toString() } - return fetch(this.baseUrl + '/api/attachments', { - method: 'POST', - body: formData - }).then((resp) => resp.json()).then((attachment) => { - this.$emit('attached', attachment) - this.$emit('update:model-value', attachment.uuid) + this.$emit('attached', attachment) + this.$emit('update:model-value', attachment.uuid) - return resolve(attachment) - }) + resolve(attachment) + } + } else { + const formData = new FormData() + + formData.append('file', file) + formData.append('submitter_slug', this.submitterSlug) + formData.append('name', 'attachments') + + return fetch(this.baseUrl + '/api/attachments', { + method: 'POST', + body: formData + }).then((resp) => resp.json()).then((attachment) => { + this.$emit('attached', attachment) + this.$emit('update:model-value', attachment.uuid) + + return resolve(attachment) + }) + } }) }) } diff --git a/app/javascript/submission_form/signature_step.vue b/app/javascript/submission_form/signature_step.vue index 05e8059b..9bfaf91f 100644 --- a/app/javascript/submission_form/signature_step.vue +++ b/app/javascript/submission_form/signature_step.vue @@ -264,6 +264,11 @@ export default { required: false, default: true }, + dryRun: { + type: Boolean, + required: false, + default: false + }, withDisclosure: { type: Boolean, required: false, @@ -563,24 +568,39 @@ export default { cropCanvasAndExportToPNG(this.$refs.canvas, { errorOnTooSmall: true }).then(async (blob) => { const file = new File([blob], 'signature.png', { type: 'image/png' }) - const formData = new FormData() + if (this.dryRun) { + const reader = new FileReader() - formData.append('file', file) - formData.append('submitter_slug', this.submitterSlug) - formData.append('name', 'attachments') - formData.append('remember_signature', this.rememberSignature) + reader.readAsDataURL(file) - return fetch(this.baseUrl + '/api/attachments', { - method: 'POST', - body: formData - }).then((resp) => resp.json()).then((attachment) => { - this.$emit('attached', attachment) - this.$emit('update:model-value', attachment.uuid) + reader.onloadend = () => { + const attachment = { url: reader.result, uuid: Math.random().toString() } - this.maybeSetSignedUuid(attachment.signed_uuid) + this.$emit('attached', attachment) + this.$emit('update:model-value', attachment.uuid) - return resolve(attachment) - }) + resolve(attachment) + } + } else { + const formData = new FormData() + + formData.append('file', file) + formData.append('submitter_slug', this.submitterSlug) + formData.append('name', 'attachments') + formData.append('remember_signature', this.rememberSignature) + + return fetch(this.baseUrl + '/api/attachments', { + method: 'POST', + body: formData + }).then((resp) => resp.json()).then((attachment) => { + this.$emit('attached', attachment) + this.$emit('update:model-value', attachment.uuid) + + this.maybeSetSignedUuid(attachment.signed_uuid) + + return resolve(attachment) + }) + } }).catch((error) => { if (error.message === 'Image too small' && this.field.required === false) { return resolve({}) diff --git a/app/javascript/template_builder/builder.vue b/app/javascript/template_builder/builder.vue index 9591b93d..72e03842 100644 --- a/app/javascript/template_builder/builder.vue +++ b/app/javascript/template_builder/builder.vue @@ -86,26 +86,55 @@ {{ t('send') }} - + + + -
+
- <%= render 'docuseal_logo' %> + <%= render 'submit_form/docuseal_logo' %>
diff --git a/app/views/submit_form/_submission_form.html.erb b/app/views/submit_form/_submission_form.html.erb index 15728cc5..2af279f8 100644 --- a/app/views/submit_form/_submission_form.html.erb +++ b/app/views/submit_form/_submission_form.html.erb @@ -1,3 +1,3 @@ <% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %> <% data_fields = (submitter.submission.template_fields || submitter.submission.template.fields).select { |f| f['submitter_uuid'] == submitter.uuid }.to_json %> - + diff --git a/app/views/submit_form/show.html.erb b/app/views/submit_form/show.html.erb index 5440b208..01a522f9 100644 --- a/app/views/submit_form/show.html.erb +++ b/app/views/submit_form/show.html.erb @@ -7,7 +7,7 @@
<%# flex block w-full sticky top-0 z-50 space-x-2 items-center bg-yellow-100 p-2 border-y border-yellow-200 %> - <%= render 'banner' %> + <%= local_assigns[:banner_html] || render('submit_form/banner') %> <% (@submitter.submission.template_schema || @submitter.submission.template.schema).each do |item| %> <% document = @submitter.submission.template_schema_documents.find { |a| a.uuid == item['attachment_uuid'] } %> <% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %> @@ -41,7 +41,7 @@ diff --git a/app/views/templates_form_preview/show.html.erb b/app/views/templates_form_preview/show.html.erb new file mode 100644 index 00000000..3ba11403 --- /dev/null +++ b/app/views/templates_form_preview/show.html.erb @@ -0,0 +1,24 @@ +<% banner_html = capture do %> + +<% end %> +<%= render template: 'submit_form/show', locals: { dry_run: true, expand: false, banner_html:, scroll_padding: '-120px' } %> diff --git a/config/routes.rb b/config/routes.rb index e58baedb..fdd24e9d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -83,6 +83,7 @@ Rails.application.routes.draw do resources :submissions, only: %i[new create] resource :folder, only: %i[edit update], controller: 'templates_folders' resource :preview, only: %i[show], controller: 'templates_preview' + resource :form, only: %i[show], controller: 'templates_form_preview' resource :code_modal, only: %i[show], controller: 'templates_code_modal' resource :preferences, only: %i[show create], controller: 'templates_preferences' resources :submissions_export, only: %i[index new]