mirror of https://github.com/docusealco/docuseal
				
				
				
			
							parent
							
								
									3604fb6461
								
							
						
					
					
						commit
						10fbdf6612
					
				@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SubmitFormInviteController < ApplicationController
 | 
				
			||||||
 | 
					  skip_before_action :authenticate_user!
 | 
				
			||||||
 | 
					  skip_authorization_check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def create
 | 
				
			||||||
 | 
					    submitter = Submitter.find_by!(slug: params[:submit_form_slug])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return head :unprocessable_entity unless can_invite?(submitter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    invite_submitters = filter_invite_submitters(submitter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ApplicationRecord.transaction do
 | 
				
			||||||
 | 
					      invite_submitters.each do |item|
 | 
				
			||||||
 | 
					        attrs = submitters_attributes.find { |e| e[:uuid] == item['uuid'] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        next unless attrs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        submitter.submission.submitters.create!(**attrs, account_id: submitter.account_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SubmissionEvents.create_with_tracking_data(submitter, 'invite_party', request, { uuid: submitter.uuid })
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      submitter.submission.update!(submitters_order: :preserved)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submitter.submission.submitters.reload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if invite_submitters.all? { |s| submitter.submission.submitters.any? { |e| e.uuid == s['uuid'] } }
 | 
				
			||||||
 | 
					      Submitters::SubmitValues.call(submitter, ActionController::Parameters.new(completed: 'true'), request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      head :ok
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      head :unprocessable_entity
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def can_invite?(submitter)
 | 
				
			||||||
 | 
					    !submitter.declined_at? &&
 | 
				
			||||||
 | 
					      !submitter.completed_at? &&
 | 
				
			||||||
 | 
					      !submitter.submission.archived_at? &&
 | 
				
			||||||
 | 
					      !submitter.submission.expired? &&
 | 
				
			||||||
 | 
					      !submitter.submission.template.archived_at?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def filter_invite_submitters(submitter)
 | 
				
			||||||
 | 
					    (submitter.submission.template_submitters || submitter.submission.template.submitters).select do |s|
 | 
				
			||||||
 | 
					      s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] }
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def submitters_attributes
 | 
				
			||||||
 | 
					    params.require(:submission).permit(submitters: [%i[uuid email]]).fetch(:submitters, [])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					export default class extends HTMLElement {
 | 
				
			||||||
 | 
					  connectedCallback () {
 | 
				
			||||||
 | 
					    this.items.forEach((item) => {
 | 
				
			||||||
 | 
					      item.addEventListener('change', (e) => {
 | 
				
			||||||
 | 
					        this.items.forEach((item) => {
 | 
				
			||||||
 | 
					          item.checked = item === e.target && e.target.checked
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get items () {
 | 
				
			||||||
 | 
					    return this.querySelectorAll('input[type="checkbox"]')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <form
 | 
				
			||||||
 | 
					    ref="form"
 | 
				
			||||||
 | 
					    action="post"
 | 
				
			||||||
 | 
					    method="post"
 | 
				
			||||||
 | 
					    class="mx-auto"
 | 
				
			||||||
 | 
					    @submit.prevent="submit"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <input
 | 
				
			||||||
 | 
					      type="hidden"
 | 
				
			||||||
 | 
					      name="authenticity_token"
 | 
				
			||||||
 | 
					      :value="authenticityToken"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      v-for="(submitter, index) in submitters"
 | 
				
			||||||
 | 
					      :key="submitter.uuid"
 | 
				
			||||||
 | 
					      :class="{ 'mt-4': index !== 0 }"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        :value="submitter.uuid"
 | 
				
			||||||
 | 
					        hidden
 | 
				
			||||||
 | 
					        name="submission[submitters][][uuid]"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					      <label
 | 
				
			||||||
 | 
					        :for="submitter.uuid"
 | 
				
			||||||
 | 
					        dir="auto"
 | 
				
			||||||
 | 
					        class="label text-2xl"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {{ t('invite') }} {{ submitter.name }}
 | 
				
			||||||
 | 
					      </label>
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        :id="submitter.uuid"
 | 
				
			||||||
 | 
					        dir="auto"
 | 
				
			||||||
 | 
					        class="base-input !text-2xl w-full"
 | 
				
			||||||
 | 
					        :placeholder="t('email')"
 | 
				
			||||||
 | 
					        type="email"
 | 
				
			||||||
 | 
					        required
 | 
				
			||||||
 | 
					        autofocus="true"
 | 
				
			||||||
 | 
					        name="submission[submitters][][email]"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      class="mt-6 md:mt-8"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <button
 | 
				
			||||||
 | 
					        type="submit"
 | 
				
			||||||
 | 
					        class="base-button w-full flex justify-center"
 | 
				
			||||||
 | 
					        :disabled="isSubmitting"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <span class="flex">
 | 
				
			||||||
 | 
					          <IconInnerShadowTop
 | 
				
			||||||
 | 
					            v-if="isSubmitting"
 | 
				
			||||||
 | 
					            class="mr-1 animate-spin"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>
 | 
				
			||||||
 | 
					            {{ t('submit') }}
 | 
				
			||||||
 | 
					          </span><span
 | 
				
			||||||
 | 
					            v-if="isSubmitting"
 | 
				
			||||||
 | 
					            class="w-6 flex justify-start mr-1"
 | 
				
			||||||
 | 
					          ><span>...</span></span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </form>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import { IconInnerShadowTop } from '@tabler/icons-vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'InviteForm',
 | 
				
			||||||
 | 
					  components: {
 | 
				
			||||||
 | 
					    IconInnerShadowTop
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  inject: ['t'],
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    submitters: {
 | 
				
			||||||
 | 
					      type: Array,
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    url: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    authenticityToken: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    submitterSlug: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  emits: ['success'],
 | 
				
			||||||
 | 
					  data () {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      isSubmitting: false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    submit () {
 | 
				
			||||||
 | 
					      this.isSubmitting = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return fetch(this.url, {
 | 
				
			||||||
 | 
					        method: 'POST',
 | 
				
			||||||
 | 
					        body: new FormData(this.$refs.form)
 | 
				
			||||||
 | 
					      }).then((response) => {
 | 
				
			||||||
 | 
					        if (response.status === 200) {
 | 
				
			||||||
 | 
					          this.$emit('success')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }).finally(() => {
 | 
				
			||||||
 | 
					        this.isSubmitting = false
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
<% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], methods: %i[url filename content_type]) %>
 | 
					<% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], 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 %>
 | 
					<% data_fields = (submitter.submission.template_fields || submitter.submission.template.fields).select { |f| f['submitter_uuid'] == submitter.uuid }.to_json %>
 | 
				
			||||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'] %>" data-completed-message="<%= configs[:completed_message].to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>"></submission-form>
 | 
					<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
 | 
				
			||||||
 | 
					<submission-form data-is-demo="<%= Docuseal.demo? %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'] %>" data-completed-message="<%= configs[:completed_message].to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>"></submission-form>
 | 
				
			||||||
 | 
				
			|||||||
					Loading…
					
					
				
		Reference in new issue