add name and phone to submitter details

pull/105/head
Alex Turchyn 2 years ago
parent b1b46cf998
commit a80e20234f

@ -13,7 +13,7 @@ module Api
Submissions.create_from_emails(template:,
user: current_user,
source: :api,
send_email: params[:send_email] != 'false',
mark_as_sent: params[:send_email] != 'false',
emails: params[:emails] || params[:email])
else
submissions_attrs = normalize_submissions_params!(submissions_params[:submission], template)
@ -21,13 +21,13 @@ module Api
Submissions.create_from_submitters(template:,
user: current_user,
source: :api,
send_email: params[:send_email] != 'false',
mark_as_sent: params[:send_email] != 'false',
submissions_attrs:)
end
submitters = submissions.flat_map(&:submitters)
send_invitation_emails(submitters) if params[:send_email] != 'false'
Submitters.send_signature_requests(submitters, send_email: params[:send_email] != 'false')
render json: submitters
rescue UnknownFieldName, UnknownSubmitterName => e
@ -36,14 +36,8 @@ module Api
private
def send_invitation_emails(submitters)
submitters.each do |submitter|
SubmitterMailer.invitation_email(submitter, message: params[:message]).deliver_later!
end
end
def submissions_params
params.permit(submission: [{ submitters: [[:uuid, :name, :email, { values: {} }]] }])
params.permit(submission: [{ submitters: [[:uuid, :name, :email, :role, :phone, { values: {} }]] }])
end
def normalize_submissions_params!(submissions_params, template)
@ -53,7 +47,8 @@ module Api
submitter[:values] =
normalize_submitter_values(template,
submitter[:values], submitter[:name] || template.submitters[index]['name'])
submitter[:values],
submitter[:role] || template.submitters[index]['name'])
end
end

@ -6,7 +6,7 @@ class EmailSettingsController < ApplicationController
def index; end
def create
if @encrypted_config.update(storage_configs)
if @encrypted_config.update(email_configs)
SettingsMailer.smtp_successful_setup(@encrypted_config.value['from_email']).deliver_now!
redirect_to settings_email_index_path, notice: 'Changes have been saved'
@ -26,7 +26,7 @@ class EmailSettingsController < ApplicationController
EncryptedConfig.find_or_initialize_by(account: current_account, key: EncryptedConfig::EMAIL_SMTP_KEY)
end
def storage_configs
def email_configs
params.require(:encrypted_config).permit(value: {}).tap do |e|
e[:value].compact_blank!
end

@ -0,0 +1,5 @@
# frozen_string_literal: true
class SmsSettingsController < ApplicationController
def index; end
end

@ -20,23 +20,19 @@ class SubmissionsController < ApplicationController
Submissions.create_from_emails(template: @template,
user: current_user,
source: :invite,
send_email: params[:send_email] == '1',
mark_as_sent: params[:send_email] == '1',
emails: params[:emails])
else
Submissions.create_from_submitters(template: @template,
user: current_user,
source: :invite,
send_email: params[:send_email] == '1',
mark_as_sent: params[:send_email] == '1',
submissions_attrs: submissions_params[:submission].to_h.values)
end
submitters = submissions.flat_map(&:submitters)
if params[:send_email] == '1'
submitters.each do |submitter|
SubmitterMailer.invitation_email(submitter, message: params[:message]).deliver_later!
end
end
Submitters.send_signature_requests(submitters, params)
redirect_to template_path(@template),
notice: "#{submitters.size} #{'recipient'.pluralize(submitters.size)} added"
@ -54,7 +50,7 @@ class SubmissionsController < ApplicationController
private
def submissions_params
params.permit(submission: { submitters: [%i[uuid email]] })
params.permit(submission: { submitters: [%i[uuid email phone name]] })
end
def load_template

@ -0,0 +1,16 @@
# frozen_string_literal: true
class SubmittersSendEmailController < ApplicationController
def create
submitter = Submitter.joins(:template)
.where(template: { account_id: current_account.id })
.find_by!(slug: params[:submitter_slug])
SubmitterMailer.invitation_email(submitter).deliver_later!
submitter.sent_at ||= Time.current
submitter.save!
redirect_back(fallback_location: submission_path(submitter.submission), notice: 'Email has been sent')
end
end

@ -9,8 +9,7 @@ window.customElements.define('submission-form', class extends HTMLElement {
this.appElem = document.createElement('div')
this.app = createApp(Form, {
submitterSlug: this.dataset.submitterSlug,
submitterUuid: this.dataset.submitterUuid,
submitter: JSON.parse(this.dataset.submitter),
authenticityToken: this.dataset.authenticityToken,
canSendEmail: this.dataset.canSendEmail === 'true',
isDirectUpload: this.dataset.isDirectUpload === 'true',

@ -294,7 +294,7 @@
v-else
:is-demo="isDemo"
:with-confetti="withConfetti"
:can-send-email="canSendEmail"
:can-send-email="canSendEmail && submitter.email"
:submitter-slug="submitterSlug"
/>
<div class="flex justify-center">
@ -343,8 +343,8 @@ export default {
}
},
props: {
submitterSlug: {
type: String,
submitter: {
type: Object,
required: true
},
canSendEmail: {
@ -352,10 +352,6 @@ export default {
required: false,
default: false
},
submitterUuid: {
type: String,
required: true
},
attachments: {
type: Array,
required: false,
@ -414,6 +410,9 @@ export default {
currentStepFields () {
return this.stepFields[this.currentStep]
},
submitterSlug () {
return this.submitter.slug
},
isAnonymousChecboxes () {
return this.currentField.type === 'checkbox' && this.currentStepFields.every((e) => !e.name) && this.currentStepFields.length > 4
},

@ -6,9 +6,11 @@
#
# id :bigint not null, primary key
# completed_at :datetime
# email :string not null
# email :string
# ip :string
# name :string
# opened_at :datetime
# phone :string
# sent_at :datetime
# slug :string not null
# ua :string

@ -59,7 +59,8 @@
{
"submitters": [
{
"name": "<%= current_account.templates.last ? current_account.templates.last.submitters.first['name'] : 'First Submitter' %>",
"name": "John Doe",
"role": "<%= current_account.templates.last ? current_account.templates.last.submitters.first['name'] : 'First Submitter' %>",
"email": "<%= current_user.email.sub('@', '+test@') %>",
"values": {
"Form Text Field Name": "Default Value"

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path>
<path d="M12 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path>
<path d="M12 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path>
</svg>

After

Width:  |  Height:  |  Size: 470 B

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M5 4h4l2 5l-2.5 1.5a11 11 0 0 0 5 5l1.5 -2.5l5 2v4a2 2 0 0 1 -2 2a16 16 0 0 1 -15 -15a2 2 0 0 1 2 -2"></path>
</svg>

After

Width:  |  Height:  |  Size: 409 B

@ -0,0 +1,28 @@
<div class="collapse collapse-plus bg-base-200">
<input type="checkbox">
<div class="collapse-title text-xl font-medium">
<div>
Documents Copy Email
</div>
</div>
<div class="collapse-content">
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
</div>
</div>

@ -0,0 +1,28 @@
<div class="collapse collapse-plus bg-base-200">
<input type="checkbox">
<div class="collapse-title text-xl font-medium">
<div>
Signature Request Email
</div>
</div>
<div class="collapse-content">
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
</div>
</div>

@ -0,0 +1,28 @@
<div class="collapse collapse-plus bg-base-200">
<input type="checkbox">
<div class="collapse-title text-xl font-medium">
<div>
Form Completed Email
</div>
</div>
<div class="collapse-content">
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
</div>
</div>

@ -2,89 +2,10 @@
<%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto">
<p class="text-4xl font-bold mb-4">Email Templates</p>
<div class="collapse collapse-plus bg-base-200 px-1">
<input type="checkbox">
<div class="collapse-title text-xl font-medium">
<div>
Signature Request Email
</div>
</div>
<div class="collapse-content">
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
</div>
</div>
<div class="collapse collapse-plus bg-base-200 px-1 mt-4">
<input type="checkbox">
<div class="collapse-title text-xl font-medium">
<div>
Form Completed Email
</div>
</div>
<div class="collapse-content">
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
</div>
</div>
<div class="collapse collapse-plus bg-base-200 px-1 mt-4">
<input type="checkbox">
<div class="collapse-title text-xl font-medium">
<div>
Documents Copy Email
</div>
</div>
<div class="collapse-content">
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
</div>
<div class="space-y-4">
<%= render 'signature_request_email_form' %>
<%= render 'submitter_completed_email_form' %>
<%= render 'documents_copy_email_form' %>
</div>
<p class="text-4xl font-bold mb-4 mt-8">Company Logo</p>
<%= render 'logo_form' %>

@ -8,7 +8,7 @@
</span>
<span class="disabled">
<span class="flex items-center justify-center space-x-2">
<%= local_assigns[:icon_disabled] || svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
<%= local_assigns[:icon_disabled] || svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span><%= disabled_with %>...</span>
</span>
</span>

@ -1,4 +1,4 @@
<div id="flash" class="absolute top-0 w-full h-0">
<div id="flash" class="absolute top-0 w-full h-0 z-20">
<div class="max-w-xl mx-auto mt-1.5">
<div class="px-4 py-3 rounded-2xl bg-base-200 flex items-center justify-between mx-4 md:mx-0">
<div class="flex items-center space-x-3">

@ -16,6 +16,9 @@
<li>
<%= link_to 'Storage', settings_storage_index_path, class: 'text-base hover:bg-base-300' %>
</li>
<li>
<%= link_to 'SMS', settings_sms_path, class: 'text-base hover:bg-base-300' %>
</li>
<% end %>
<li>
<%= link_to 'E-Signature', settings_esign_path, class: 'text-base hover:bg-base-300' %>

@ -0,0 +1,11 @@
<div class="alert">
<%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div>
<p class="font-bold">Send signature requests via SMS</p>
<p class="text-gray-700">
Unlock with DocuSeal Enterprise
<br>
<a class="link font-medium" target="_blank" href="<%= "#{Docuseal::CONSOLE_URL}/#{Docuseal.multitenant? ? 'plans' : 'on_premise'}" %>">Learn More</a>
</p>
</div>
</div>

@ -0,0 +1,8 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto">
<h1 class="text-4xl font-bold mb-4">SMS</h1>
<%= render 'placeholder' %>
</div>
<div class="w-0 md:w-52"></div>
</div>

@ -0,0 +1,43 @@
<%= form_for '', url: template_submissions_path(template), html: { class: 'space-y-4', autocomplete: 'off' }, data: { turbo_frame: :_top } do |f| %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-200" data-targets="dynamic-list.items">
<div class="card-body">
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="<%= template.submitters.size == 1 ? 'right-2' : '-top-3' %> relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
</a>
</div>
<div class="grid <%= 'md:grid-cols-2' if template.submitters.size > 1 %> gap-4">
<% template.submitters.each do |item| %>
<div class="form-control">
<% if template.submitters.size > 1 %>
<label class="label pt-0 pb-1 text-xs">
<span class="label-text"> <%= item['name'] %></span>
</label>
<% end %>
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="input input-sm input-bordered" placeholder="Name" required>
<div class="grid <%= 'md:grid-cols-2 gap-1' if template.submitters.size == 1 %>">
<input type="email" name="submission[1][submitters][][email]" autocomplete="off" class="input input-sm input-bordered mt-1.5" placeholder="Email (optional)">
<input type="tel" pattern="^\+[0-9\s\-]+$" oninvalid="this.value ? this.setCustomValidity('Use internatioanl format: +1xxx...') : ''" oninput="this.setCustomValidity('')" name="submission[1][submitters][][phone]" autocomplete="off" class="input input-sm input-bordered mt-1.5" placeholder="Phone (optional)">
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add Recipient</span>
</a>
</dynamic-list>
<div>
<%= render 'send_email', f:, template: %>
<%= render 'send_sms', f: %>
</div>
<div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %>
</div>
<% end %>

@ -0,0 +1,42 @@
<%= form_for '', url: template_submissions_path(template), html: { class: 'space-y-4', autocomplete: 'off' }, data: { turbo_frame: :_top } do |f| %>
<% if template.submitters.size == 1 %>
<div class="form-control">
<autoresize-textarea>
<%= f.text_area :emails, required: true, class: 'base-textarea w-full', placeholder: 'Type emails here...' %>
</autoresize-textarea>
</div>
<% else %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-200" data-targets="dynamic-list.items">
<div class="card-body">
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="-top-3 relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
</a>
</div>
<div class="grid md:grid-cols-2 gap-4">
<% template.submitters.each do |item| %>
<div class="form-control">
<label class="label pt-0 pb-1 text-xs">
<span class="label-text"> <%= item['name'] %></span>
</label>
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<input type="email" name="submission[1][submitters][][email]" autocomplete="off" class="input input-sm input-bordered" placeholder="Email" required>
</div>
<% end %>
</div>
</div>
</div>
</div>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add Recipient</span>
</a>
</dynamic-list>
<% end %>
<%= render 'send_email', f:, template: %>
<div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %>
</div>
<% end %>

@ -0,0 +1,44 @@
<%= form_for '', url: template_submissions_path(template), html: { class: 'space-y-4', autocomplete: 'off' }, data: { turbo_frame: :_top } do |f| %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-200" data-targets="dynamic-list.items">
<div class="card-body">
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="<%= template.submitters.size == 1 ? 'right-2' : '-top-3' %> relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
</a>
</div>
<div class="grid md:grid-cols-2 <%= template.submitters.size > 1 ? 'gap-4' : 'gap-1' %>">
<% template.submitters.each do |item| %>
<div class="form-control">
<% if template.submitters.size > 1 %>
<label class="label pt-0 pb-1 text-xs">
<span class="label-text"> <%= item['name'] %></span>
</label>
<% end %>
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<input type="tel" pattern="^\+[0-9\s\-]+$" oninvalid="this.value ? this.setCustomValidity('Use internatioanl format: +1xxx...') : ''" oninput="this.setCustomValidity('')" name="submission[1][submitters][][phone]" autocomplete="off" class="input input-sm input-bordered" placeholder="Phone" required>
<% if template.submitters.size > 1 %>
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="input input-sm input-bordered mt-1.5" placeholder="Name (optional)">
<% end %>
</div>
<% if template.submitters.size == 1 %>
<div class="form-control flex">
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="input input-sm input-bordered" placeholder="Name (optional)">
</div>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add Recipient</span>
</a>
</dynamic-list>
<%= render 'send_sms', f: %>
<div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %>
</div>
<% end %>

@ -0,0 +1,37 @@
<div class="form-control">
<% is_smtp_configured = Accounts.can_send_emails?(current_account) %>
<%= f.label :send_email, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !is_smtp_configured, onchange: "window.message_field && message_field.classList.toggle('hidden', !event.currentTarget.checked)" %>
<span class="label">Send Email</span>
<% end %>
<% unless is_smtp_configured %>
<div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div>
<p class="font-bold">SMTP not Configured</p>
<p class="text-gray-700">
Configure SMTP settings in order to send emails:
<br>
<a class="link font-medium" data-turbo-frame="_top" href="<%= settings_email_index_path %>">Go to SMTP settings</a>
</p>
</div>
</div>
<% end %>
</div>
<% unless AccountConfig.exists?(key: AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY) %>
<div id="message_field" class="card card-compact bg-base-200 hidden">
<div class="card-body">
<div class="form-control space-y-2">
<span class="label-text">Hi there,</span>
<autoresize-textarea>
<%= f.text_area :message, value: format(SubmitterMailer::DEFAULT_MESSAGE, name: template.name), required: true, class: 'base-textarea !rounded-lg w-full' %>
</autoresize-textarea>
<span class="label-text">
Thanks,
<br>
<%= current_account.name %>
</span>
</div>
</div>
</div>
<% end %>

@ -0,0 +1,3 @@
<div class="form-control">
<%= render 'sms_settings/placeholder' %>
</div>

@ -0,0 +1,5 @@
<div class="mt-2 mb-1">
<div class="tooltip w-full" data-tip="Unlock with Enterpise">
<%= link_to submitter.sent_at? ? 'Re-send SMS' : 'Send SMS', "#{Docuseal::CONSOLE_URL}/#{Docuseal.multitenant? ? 'plans' : 'on_premise'}", class: 'btn btn-sm btn-primary text-gray-400 w-full' %>
</div>
</div>

@ -1,81 +1,26 @@
<%= render 'shared/turbo_modal', title: 'New Recipients' do %>
<%= form_for '', url: template_submissions_path(@template), html: { class: 'space-y-4', autocomplete: 'off' }, data: { turbo_frame: :_top } do |f| %>
<% if @template.submitters.size == 1 %>
<div class="form-control">
<%= f.label :emails, class: 'label' %>
<autoresize-textarea>
<%= f.text_area :emails, required: true, class: 'base-textarea w-full' %>
</autoresize-textarea>
</div>
<% else %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-200" data-targets="dynamic-list.items">
<div class="card-body">
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="-top-3 relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
</a>
</div>
<div class="grid md:grid-cols-2 gap-4">
<% @template.submitters.each do |item| %>
<div class="form-control">
<label class="label pt-0 pb-1 text-xs">
<span class="label-text"> <%= item['name'] %></span>
</label>
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<input type="email" name="submission[1][submitters][][email]" value="<%= item['email'] %>" autocomplete="off" class="input input-sm input-bordered" placeholder="Email" required>
</div>
<% end %>
</div>
</div>
</div>
</div>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add Recipient</span>
</a>
</dynamic-list>
<% end %>
<div class="form-control">
<% is_smtp_configured = Accounts.can_send_emails?(current_account) %>
<%= f.label :send_email, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :send_email, class: 'base-checkbox', disabled: !is_smtp_configured, onchange: "window.message_field && message_field.classList.toggle('hidden', !event.currentTarget.checked)" %>
<span class="label">Send Email</span>
<% end %>
<% unless is_smtp_configured %>
<div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div>
<p class="font-bold">SMTP not Configured</p>
<p class="text-gray-700">
Configure SMTP settings in order to send emails:
<br>
<a class="link font-medium" data-turbo-frame="_top" href="<%= settings_email_index_path %>">Go to SMTP settings</a>
</p>
</div>
</div>
<%= render 'shared/turbo_modal', title: 'Add New Recipients' do %>
<% options = [['via Email', 'email'], ['via Phone', 'phone'], ['Detailed', 'detailed']] %>
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center mt-4 block">
<div class="join">
<% options.each_with_index do |(label, value), index| %>
<span>
<%= radio_button_tag 'option', value, value == 'email', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %>
<label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-focus btn-sm join-item w-28 peer-checked:btn-active normal-case">
<%= label %>
</label>
</span>
<% end %>
</div>
<% unless AccountConfig.exists?(key: AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY) %>
<div id="message_field" class="card card-compact bg-base-200 hidden">
<div class="card-body">
<div class="form-control space-y-2">
<span class="label-text">Hi there,</span>
<autoresize-textarea>
<%= f.text_area :message, value: format(SubmitterMailer::DEFAULT_MESSAGE, name: @template.name), required: true, class: 'base-textarea !rounded-lg w-full' %>
</autoresize-textarea>
<span class="label-text">
Thanks,
<br>
<%= current_account.name %>
</span>
</div>
</div>
</div>
<% end %>
<div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %>
</toggle-visible>
<div class="mt-4">
<div id="email">
<%= render 'email_form', template: @template %>
</div>
<div id="phone" class="hidden">
<%= render 'phone_form', template: @template %>
</div>
<div id="detailed" class="hidden">
<%= render 'detailed_form', template: @template %>
</div>
<% end %>
</div>
<% end %>

@ -72,18 +72,44 @@
<%= (@submission.template_submitters || @submission.template.submitters).find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %>
</span>
</div>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('mail', class: 'w-5 h-5') %>
<span>
<%= submitter&.email || 'N/A' %>
</span>
</div>
<% if submitter&.name.present? %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('user', class: 'w-5 h-5') %>
<span>
<%= submitter&.name %>
</span>
</div>
<% end %>
<% if submitter&.email.present? %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('mail', class: 'w-5 h-5') %>
<span>
<%= submitter.email || 'N/A' %>
</span>
</div>
<% end %>
<% if submitter&.phone.present? %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('phone', class: 'w-5 h-5') %>
<span>
<%= submitter.phone %>
</span>
</div>
<% end %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('writing', class: 'w-5 h-5') %>
<span>
<%= submitter&.completed_at? ? l(submitter.completed_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) : 'Not completed yet' %>
</span>
</div>
<% if submitter && submitter.email && !submitter.completed_at %>
<div class="mt-2 mb-1">
<%= button_to button_title(title: submitter.sent_at? ? 'Re-send Email' : 'Send Email', disabled_with: 'Sending'), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %>
</div>
<% end %>
<% if submitter && submitter.phone && !submitter.completed_at %>
<%= render 'send_sms_button', submitter: %>
<% end %>
<% if submitter && !submitter.completed_at? %>
<div class="mt-2 mb-1">
<a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_path(slug: submitter.slug) %>">

@ -17,7 +17,7 @@
</div>
</div>
<div>
<% if Accounts.can_send_emails?(@submitter.submission.template.account) %>
<% if Accounts.can_send_emails?(@submitter.submission.template.account) && @submitter.email.present? %>
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'white-button w-full' %>
<div class="divider">OR</div>
<% end %>

@ -33,7 +33,7 @@
<div class="fixed bottom-0 w-full h-0 z-20">
<div class="mx-auto" style="max-width: 1000px">
<div class="relative md:mx-32">
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" 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 %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter="<%= @submitter.to_json(only: %i[uuid slug email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" 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 %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
</div>
</div>
</div>

@ -0,0 +1,116 @@
<% status_badges = { 'awaiting' => 'badge-info', 'sent' => 'badge-info', 'completed' => 'badge-success', 'opened' => 'badge-warning' } %>
<a href="<%= submission_path(submission) %>" class="bg-base-200 w-full flex flex-col md:flex-row space-y-4 md:space-y-0 md:justify-between rounded-2xl px-6 py-5 md:items-center">
<% submitters = (submission.template_submitters || submission.template.submitters).filter_map { |item| submission.submitters.find { |e| e.uuid == item['uuid'] } } %>
<% is_submission_completed = submitters.all?(&:completed_at?) %>
<% if submitters.size == 1 %>
<div>
<% submitter = submitters.first %>
<div class="flex items-center space-x-4">
<span class="flex items-center space-x-3">
<div class="tooltip flex" data-tip="<%= l(submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge <%= status_badges[submitter.status] %> md:w-32 badge-lg bg-opacity-50 uppercase text-sm font-semibold">
<%= submitter.status %>
</span>
</div>
<span class="text-lg break-all flex items-center">
<%= submitter.name || submitter.email || submitter.phone %>
</span>
</span>
</div>
</div>
<div class="flex space-x-2 items-center">
<% if submitter.completed_at? %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(submitter.slug) %>" class="btn btn-sm btn-neutral text-white md:w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-5 h-5 stroke-2') %>
<span class="inline">Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span class="inline">Downloa...</span>
</span>
</download-button>
</button>
</form>
<% else %>
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug), class: 'btn btn-sm btn-neutral text-white md:w-36 flex', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Link', copy_title_md: 'Copy', copied_title_md: 'Copied' %>
<% end %>
<span class="btn btn-outline btn-sm w-20 md:w-24">View</span>
<%= button_to button_title(title: nil, disabled_with: 'Remov', icon: svg_icon('trash', class: 'w-6 h-6')), submission_path(submission), class: 'btn btn-outline btn-sm', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
</div>
<% else %>
<div class="space-y-1 w-full md:mr-2">
<div class="flex items-center space-x-3">
<% if is_submission_completed %>
<% latest_submitter = submitters.select(&:completed_at?).max_by(&:completed_at) %>
<div class="tooltip flex" data-tip="<%= l(latest_submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge <%= status_badges[latest_submitter.status] %> md:w-32 bg-opacity-50 badge-lg uppercase text-sm font-semibold">
<%= latest_submitter.status %>
</span>
</div>
<% end %>
<div class="w-full">
<% submitters.each_with_index do |submitter, index| %>
<div class="flex justify-between items-center space-x-3">
<span class="flex items-center space-x-3">
<% unless is_submission_completed %>
<div class="tooltip flex" data-tip="<%= l(submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge md:w-24 <%= status_badges[submitter.status] %> bg-opacity-50 uppercase text-xs font-semibold">
<%= submitter.status %>
</span>
</div>
<% end %>
<span class="text-lg break-all">
<%= submitter.name || submitter.email || submitter.phone %>
</span>
</span>
<% if submitter.completed_at? && !is_submission_completed %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(submitter.slug) %>" class="btn btn-xs btn-neutral text-white md:w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-4 h-4 stroke-2') %>
<span class="hidden md:inline">Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin') %>
<span class="hidden md:inline">Downloa...</span>
</span>
</download-button>
</button>
</form>
<% elsif !is_submission_completed %>
<div class="flex items-center space-x-3">
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug), class: 'btn btn-xs text-xs btn-neutral text-white md:w-36 flex', icon_class: 'w-4 h-4 text-white', copy_title: 'Copy Link' %>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
</div>
<div class="flex space-x-2 items-center">
<% if is_submission_completed %>
<% latest_submitter = submitters.select(&:completed_at?).max_by(&:completed_at) %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(latest_submitter.slug) %>" class="btn btn-sm btn-neutral text-white md:w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-5 h-5 stroke-2') %>
<span class="inline">Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span class="inline">Downloa...</span>
</span>
</download-button>
</button>
</form>
<% end %>
<span class="btn btn-outline btn-sm w-20 md:w-24">View</span>
<%= button_to button_title(title: nil, disabled_with: 'Remov', icon: svg_icon('trash', class: 'w-6 h-6')), submission_path(submission), class: 'btn btn-outline btn-sm', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
</div>
<% end %>
</a>

@ -38,126 +38,9 @@
</div>
</div>
<% end %>
<% status_badges = { 'awaiting' => 'badge-info', 'sent' => 'badge-info', 'completed' => 'badge-success', 'opened' => 'badge-warning' } %>
<% if @submissions.present? %>
<div class="space-y-4">
<% @submissions.each do |submission| %>
<a href="<%= submission_path(submission) %>" class="bg-base-200 w-full flex flex-col md:flex-row space-y-4 md:space-y-0 md:justify-between rounded-2xl px-6 py-5 md:items-center">
<% submitters = (submission.template_submitters || submission.template.submitters).filter_map { |item| submission.submitters.find { |e| e.uuid == item['uuid'] } } %>
<% is_submission_completed = submitters.all?(&:completed_at?) %>
<% if submitters.size == 1 %>
<div>
<% submitter = submitters.first %>
<div class="flex items-center space-x-4">
<span class="flex items-center space-x-3">
<div class="tooltip flex" data-tip="<%= l(submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge <%= status_badges[submitter.status] %> md:w-32 badge-lg bg-opacity-50 uppercase text-sm font-semibold">
<%= submitter.status %>
</span>
</div>
<span class="text-lg break-all">
<%= submitter.email %>
</span>
</span>
</div>
</div>
<div class="flex space-x-2 items-center">
<% if submitter.completed_at? %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(submitter.slug) %>" class="btn btn-sm btn-neutral text-white md:w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-5 h-5 stroke-2') %>
<span class="inline">Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span class="inline">Downloa...</span>
</span>
</download-button>
</button>
</form>
<% else %>
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug), class: 'btn btn-sm btn-neutral text-white md:w-36 flex', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Link' %>
<% end %>
<span class="btn btn-outline btn-sm w-20 md:w-24">View</span>
<%= button_to button_title(title: nil, disabled_with: 'Remov', icon: svg_icon('trash', class: 'w-6 h-6')), submission_path(submission), class: 'btn btn-outline btn-sm', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
</div>
<% else %>
<div class="space-y-1 w-full md:mr-2">
<div class="flex items-center space-x-3">
<% if is_submission_completed %>
<% latest_submitter = submitters.select(&:completed_at?).max_by(&:completed_at) %>
<div class="tooltip flex" data-tip="<%= l(latest_submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge <%= status_badges[latest_submitter.status] %> md:w-32 bg-opacity-50 badge-lg uppercase text-sm font-semibold">
<%= latest_submitter.status %>
</span>
</div>
<% end %>
<div class="w-full">
<% submitters.each_with_index do |submitter, index| %>
<div class="flex justify-between items-center space-x-3">
<span class="flex items-center space-x-3">
<% unless is_submission_completed %>
<div class="tooltip flex" data-tip="<%= l(submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge md:w-24 <%= status_badges[submitter.status] %> bg-opacity-50 uppercase text-xs font-semibold">
<%= submitter.status %>
</span>
</div>
<% end %>
<span class="text-lg break-all">
<%= submitter.email %>
</span>
</span>
<% if submitter.completed_at? && !is_submission_completed %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(submitter.slug) %>" class="btn btn-xs btn-neutral text-white md:w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-4 h-4 stroke-2') %>
<span class="hidden md:inline">Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin') %>
<span class="hidden md:inline">Downloa...</span>
</span>
</download-button>
</button>
</form>
<% elsif !is_submission_completed %>
<div class="flex items-center space-x-3">
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug), class: 'btn btn-xs text-xs btn-neutral text-white md:w-36 flex', icon_class: 'w-4 h-4 text-white', copy_title: 'Copy Link' %>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
</div>
<div class="flex space-x-2 items-center">
<% if is_submission_completed %>
<% latest_submitter = submitters.select(&:completed_at?).max_by(&:completed_at) %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(latest_submitter.slug) %>" class="btn btn-sm btn-neutral text-white md:w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-5 h-5 stroke-2') %>
<span class="inline">Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span class="inline">Downloa...</span>
</span>
</download-button>
</button>
</form>
<% end %>
<span class="btn btn-outline btn-sm w-20 md:w-24">View</span>
<%= button_to button_title(title: nil, disabled_with: 'Remov', icon: svg_icon('trash', class: 'w-6 h-6')), submission_path(submission), class: 'btn btn-outline btn-sm', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
</div>
<% end %>
</a>
<% end %>
<%= render partial: 'submission', collection: @submissions %>
</div>
<%= render 'shared/pagination', pagy: @pagy, items_name: 'submissions' %>
<% else %>

@ -64,6 +64,7 @@ Rails.application.routes.draw do
resources :submitters, only: %i[], param: 'slug' do
resources :download, only: %i[index], controller: 'submissions_download'
resources :send_email, only: %i[create], controller: 'submitters_send_email'
resources :debug, only: %i[index], controller: 'submissions_debug' if Rails.env.development?
end
@ -71,6 +72,7 @@ Rails.application.routes.draw do
unless Docuseal.multitenant?
resources :storage, only: %i[index create], controller: 'storage_settings'
resources :email, only: %i[index create], controller: 'email_settings'
resources :sms, only: %i[index], controller: 'sms_settings'
end
resource :esign, only: %i[show create new update destroy], controller: 'esign_settings'
resources :users, only: %i[index]

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddPhoneAndNameToSubmitters < ActiveRecord::Migration[7.0]
def change
add_column :submitters, :name, :string
add_column :submitters, :phone, :string
change_column_null :submitters, :email, true
end
end

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_08_19_190316) do
ActiveRecord::Schema[7.0].define(version: 2023_09_02_171216) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -108,7 +108,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_19_190316) do
create_table "submitters", force: :cascade do |t|
t.bigint "submission_id", null: false
t.string "uuid", null: false
t.string "email", null: false
t.string "email"
t.string "slug", null: false
t.text "values", null: false
t.string "ua"
@ -118,6 +118,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_19_190316) do
t.datetime "completed_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.string "phone"
t.index ["email"], name: "index_submitters_on_email"
t.index ["slug"], name: "index_submitters_on_slug", unique: true
t.index ["submission_id"], name: "index_submitters_on_submission_id"

@ -16,18 +16,22 @@ module ReplaceEmailVariables
slug: submitter.slug, **Docuseal.default_url_options
)
submission_link =
Rails.application.routes.url_helpers.submission_url(
submitter.submission, **Docuseal.default_url_options
)
if submitter.submission
submission_link =
Rails.application.routes.url_helpers.submission_url(
submitter.submission, **Docuseal.default_url_options
)
end
text = text.gsub(TEMAPLTE_NAME, submitter.template.name)
text = text.gsub(SUBMITTER_EMAIL, submitter.email)
text = text.gsub(TEMAPLTE_NAME, submitter.template.name) if submitter.template
text = text.gsub(SUBMITTER_EMAIL, submitter.email) if submitter.email
text = text.gsub(SUBMITTER_LINK, submitter_link)
text = text.gsub(SUBMISSION_LINK, submission_link)
text = text.gsub(SUBMISSION_LINK, submission_link) if submission_link
text = text.gsub(DOCUMENTS_LINKS, build_documents_links_text(submitter))
text.gsub(ACCOUNT_NAME, submitter.template.account.name)
text.gsub(ACCOUNT_NAME, submitter.template.account.name) if submitter.template
text
end
def build_documents_links_text(submitter)

@ -11,33 +11,35 @@ module Submissions
submission.save!
end
def create_from_emails(template:, user:, emails:, source:, send_email: false)
def create_from_emails(template:, user:, emails:, source:, mark_as_sent: false)
emails = emails.to_s.scan(User::EMAIL_REGEXP) unless emails.is_a?(Array)
emails.map do |email|
submission = template.submissions.new(created_by_user: user, source:, template_submitters: template.submitters)
submission.submitters.new(email:,
uuid: template.submitters.first['uuid'],
sent_at: send_email ? Time.current : nil)
sent_at: mark_as_sent ? Time.current : nil)
submission.tap(&:save!)
end
end
def create_from_submitters(template:, user:, submissions_attrs:, source:, send_email: false)
def create_from_submitters(template:, user:, submissions_attrs:, source:, mark_as_sent: false)
submissions_attrs.map do |attrs|
submission = template.submissions.new(created_by_user: user, source:, template_submitters: template.submitters)
attrs[:submitters].each_with_index do |submitter_attrs, index|
uuid =
submitter_attrs[:uuid].presence ||
template.submitters.find { |e| e['name'] == submitter_attrs[:name] }&.dig('uuid') ||
template.submitters.find { |e| e['name'] == submitter_attrs[:role] }&.dig('uuid') ||
template.submitters[index]&.dig('uuid')
next if uuid.blank?
submission.submitters.new(email: submitter_attrs[:email],
sent_at: send_email ? Time.current : nil,
phone: submitter_attrs[:phone].to_s.gsub(/[^0-9+]/, ''),
name: submitter_attrs[:name],
sent_at: mark_as_sent && submitter_attrs[:email].present? ? Time.current : nil,
values: submitter_attrs[:values] || {},
uuid:)
end

@ -91,15 +91,23 @@ module Submissions
def build_submission_data(submitter, submitter_name, submitters_count)
[
{
name: column_name('Name', submitter_name, submitters_count),
value: submitter.name
},
{
name: column_name('Email', submitter_name, submitters_count),
value: submitter.email
},
{
name: column_name('Phone', submitter_name, submitters_count),
value: submitter.phone
},
{
name: column_name('Completed At', submitter_name, submitters_count),
value: submitter.completed_at
}
]
].reject { |e| e[:value].blank? }
end
def column_name(name, submitter_name, submitters_count = 1)

@ -28,4 +28,14 @@ module Submitters
record: submitter
)
end
def send_signature_requests(submitters, params)
return unless params[:send_email] == true || params[:send_email] == '1'
submitters.each do |submitter|
next if submitter.email.blank?
SubmitterMailer.invitation_email(submitter, message: params[:message]).deliver_later!
end
end
end

@ -14,7 +14,7 @@ module Submitters
.except('uuid', 'values', 'slug')
.merge('values' => values,
'documents' => documents,
'submitter_name' => submitter_name)
'role' => submitter_name)
end
def build_values_array(submitter)

@ -27,12 +27,12 @@ RSpec.describe 'Submit Form' do
click_button 'Start'
fill_in 'First Name', with: 'Adam'
click_on 'Next'
click_on 'next'
click_on 'type_text_button'
fill_in 'signature_text_input', with: 'Adam'
expect do
click_on 'Submit'
click_on 'submit'
end.not_to(change(Submitter, :count))
submitter = Submitter.find_by(email: 'john.dou@example.com')
@ -58,10 +58,10 @@ RSpec.describe 'Submit Form' do
it 'completes the form' do
fill_in 'First Name', with: 'Sally'
click_on 'Next'
click_on 'next'
click_on 'type_text_button'
fill_in 'signature_text_input', with: 'Sally'
click_on 'Submit'
click_on 'submit'
submitter.reload
@ -75,12 +75,12 @@ RSpec.describe 'Submit Form' do
it 'sends completed email' do
fill_in 'First Name', with: 'Adam'
click_on 'Next'
click_on 'next'
click_on 'type_text_button'
fill_in 'signature_text_input', with: 'Adam'
expect do
click_on 'Submit'
click_on 'submit'
end.to change(enqueued_jobs, :size).by(3)
end
end

Loading…
Cancel
Save