Skip to content

Commit 7439ef0

Browse files
authored
Merge from docusealco/wip
2 parents 6e685bb + fc4b4de commit 7439ef0

28 files changed

+278
-88
lines changed

app/controllers/api/tools_controller.rb

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def verify
3434
}
3535
end
3636
}
37+
rescue HexaPDF::MalformedPDFError
38+
render json: { error: 'Malformed PDF' }, status: :unprocessable_entity
3739
end
3840
end
3941
end

app/controllers/application_controller.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
3030
redirect_to request.referer, alert: 'Too many requests', status: :too_many_requests
3131
end
3232

33-
if Rails.env.production?
33+
if Rails.env.production? || Rails.env.test?
3434
rescue_from CanCan::AccessDenied do |e|
3535
Rollbar.warning(e) if defined?(Rollbar)
3636

app/controllers/send_submission_email_controller.rb

+1-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ class SendSubmissionEmailController < ApplicationController
99

1010
SEND_DURATION = 30.minutes
1111

12-
def success; end
13-
1412
def create
1513
@submitter =
1614
if params[:template_slug]
@@ -31,7 +29,7 @@ def create
3129
end
3230

3331
respond_to do |f|
34-
f.html { redirect_to success_send_submission_email_index_path }
32+
f.html { render :success }
3533
f.json { head :ok }
3634
end
3735
end

app/controllers/submissions_preview_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def show
4040

4141
def completed
4242
@submission = Submission.find_by!(slug: params[:submissions_preview_slug])
43+
@template = @submission.template
4344

4445
render :completed, layout: 'form'
4546
end

app/controllers/templates_preferences_controller.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ def template_params
2828
submitters_order
2929
completed_notification_email_subject completed_notification_email_body
3030
completed_notification_email_enabled completed_notification_email_attach_audit] +
31-
[completed_message: %i[title body]]
31+
[completed_message: %i[title body],
32+
submitters: [%i[uuid request_email_subject request_email_body]]]
3233
).tap do |attrs|
34+
attrs[:preferences].delete(:submitters) if params[:request_email_per_submitter] != '1'
3335
attrs[:preferences] = attrs[:preferences].transform_values do |value|
3436
if %w[true false].include?(value)
3537
value == 'true'

app/javascript/elements/autoresize_textarea.js

+24
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export default class extends HTMLElement {
33
this.resize()
44

55
this.textarea.addEventListener('input', () => this.resize())
6+
7+
this.observeVisibility()
68
}
79

810
resize () {
@@ -11,6 +13,28 @@ export default class extends HTMLElement {
1113
}
1214
}
1315

16+
observeVisibility () {
17+
this.observer = new IntersectionObserver(
18+
(entries) => {
19+
entries.forEach((entry) => {
20+
if (entry.isIntersecting) {
21+
this.resize()
22+
this.observer.unobserve(this.textarea)
23+
}
24+
})
25+
},
26+
{
27+
threshold: 0.1
28+
}
29+
)
30+
31+
this.observer.observe(this.textarea)
32+
}
33+
34+
disconnectedCallback () {
35+
this.observer.unobserve(this.textarea)
36+
}
37+
1438
get textarea () {
1539
return this.querySelector('textarea')
1640
}

app/javascript/submission_form/area.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
class="field-area flex absolute lg:text-base -outline-offset-1"
44
dir="auto"
55
:style="computedStyle"
6-
:class="{ 'font-serif': field.preferences?.font === 'Times', 'text-[1.6vw] lg:text-base': !textOverflowChars, 'text-[1.0vw] lg:text-xs': textOverflowChars, 'cursor-default': !submittable, 'border border-red-100 bg-red-100 cursor-pointer': submittable, 'border border-red-100': !isActive && submittable, 'bg-opacity-80': !isActive && !isValueSet && submittable, 'field-area-active outline-red-500 outline-dashed outline-2 z-10': isActive && submittable, 'bg-opacity-40': (isActive || isValueSet) && submittable }"
6+
:class="{ 'font-mono': field.preferences?.font === 'Courier', 'font-serif': field.preferences?.font === 'Times', 'text-[1.6vw] lg:text-base': !textOverflowChars, 'text-[1.0vw] lg:text-xs': textOverflowChars, 'cursor-default': !submittable, 'border border-red-100 bg-red-100 cursor-pointer': submittable, 'border border-red-100': !isActive && submittable, 'bg-opacity-80': !isActive && !isValueSet && submittable, 'field-area-active outline-red-500 outline-dashed outline-2 z-10': isActive && submittable, 'bg-opacity-40': (isActive || isValueSet) && submittable }"
77
>
88
<div
99
v-if="(!withFieldPlaceholder || !field.name || field.type === 'cells') && !isActive && !isValueSet && field.type !== 'checkbox' && submittable && !area.option_uuid"

app/javascript/submission_form/form.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@
100100
<button
101101
v-if="!isCompleted"
102102
id="minimize_form_button"
103-
class="absolute right-0 mr-2 mt-2 top-0 hidden md:block"
103+
class="absolute right-0 top-0"
104+
:class="currentField?.description?.length > 100 ? 'mr-1 mt-1 md:mr-2 md:mt-2': 'mr-2 mt-2 hidden md:block'"
104105
:title="t('minimize')"
105106
@click.prevent="minimizeForm"
106107
>

app/javascript/template_builder/builder.vue

-1
Original file line numberDiff line numberDiff line change
@@ -1401,7 +1401,6 @@ export default {
14011401
const lastArea = field.areas[field.areas.length - 1]
14021402
14031403
if (lastArea) {
1404-
fieldArea.x -= lastArea.w / 2
14051404
fieldArea.w = lastArea.w
14061405
fieldArea.h = lastArea.h
14071406
}

app/mailers/submitter_mailer.rb

+29-10
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,20 @@ def invitation_email(submitter)
1414
@email_message = submitter.account.email_messages.find_by(uuid: submitter.preferences['email_message_uuid'])
1515
end
1616

17-
@body = @email_message&.body.presence || @submitter.template.preferences['request_email_body'].presence
18-
@subject = @email_message&.subject.presence || @submitter.template.preferences['request_email_subject'].presence
17+
template_submitters_index =
18+
if @email_message.blank?
19+
build_submitter_preferences_index(@submitter)
20+
else
21+
{}
22+
end
23+
24+
@body = @email_message&.body.presence ||
25+
template_submitters_index.dig(@submitter.uuid, 'request_email_body').presence ||
26+
@submitter.template.preferences['request_email_body'].presence
27+
28+
@subject = @email_message&.subject.presence ||
29+
template_submitters_index.dig(@submitter.uuid, 'request_email_subject').presence ||
30+
@submitter.template.preferences['request_email_subject'].presence
1931

2032
@email_config = AccountConfigs.find_for_account(@current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY)
2133

@@ -24,14 +36,7 @@ def invitation_email(submitter)
2436
reply_to = build_submitter_reply_to(@submitter)
2537

2638
I18n.with_locale(@current_account.locale) do
27-
subject =
28-
if @email_config || @subject
29-
ReplaceEmailVariables.call(@subject || @email_config.value['subject'], submitter:)
30-
elsif @submitter.with_signature_fields?
31-
I18n.t(:you_are_invited_to_sign_a_document)
32-
else
33-
I18n.t(:you_are_invited_to_submit_a_form)
34-
end
39+
subject = build_invite_subject(@subject, @email_config, submitter)
3540

3641
mail(
3742
to: @submitter.friendly_name,
@@ -196,6 +201,20 @@ def normalize_user_email(user)
196201
user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name
197202
end
198203

204+
def build_invite_subject(subject, email_config, submitter)
205+
if email_config || subject
206+
ReplaceEmailVariables.call(subject || email_config.value['subject'], submitter:)
207+
elsif submitter.with_signature_fields?
208+
I18n.t(:you_are_invited_to_sign_a_document)
209+
else
210+
I18n.t(:you_are_invited_to_submit_a_form)
211+
end
212+
end
213+
214+
def build_submitter_preferences_index(submitter)
215+
submitter.template.preferences['submitters'].to_a.index_by { |e| e['uuid'] }
216+
end
217+
199218
def add_attachments_with_size_limit(submitter, storage_attachments, current_size, filename_format = nil)
200219
total_size = current_size
201220

app/views/icons/_mail_opened.html.erb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
2+
<path d="M3 9l9 6l9 -6l-9 -6l-9 6"></path>
3+
<path d="M21 9v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10"></path>
4+
<path d="M3 19l6 -6"></path>
5+
<path d="M15 13l6 6"></path>
6+
</svg>

app/views/send_submission_email/success.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="space-y-6 mx-auto">
33
<div class="space-y-6">
44
<div class="flex items-center justify-center">
5-
<%= render 'start_form/docuseal_logo' %>
5+
<%= render 'start_form/banner' %>
66
</div>
77
<div class="text-center text-4xl font-bold">
88
<%= t('email_has_been_sent') %>

app/views/submissions/_send_email.html.erb

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<% submitter_preferences_index = template.preferences['submitters'].to_a.index_by { |e| e['uuid'] } %>
12
<div class="form-control">
23
<% can_send_emails = Accounts.can_send_emails?(current_account) %>
34
<div class="flex justify-between items-center">
@@ -36,10 +37,18 @@
3637
<% config = AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY) %>
3738
<div id="message_field" class="card card-compact bg-base-300/40 hidden">
3839
<div class="card-body">
39-
<div class="form-control space-y-2">
40+
<%= tag.input id: 'request_email_per_submitter', value: '1', name: 'request_email_per_submitter', class: 'peer', type: 'checkbox', hidden: true, checked: local_assigns[:message_per_submitter] != false && template.preferences['submitters'].to_a.size > 1 %>
41+
<div class="peer-checked:hidden form-control space-y-2">
4042
<div class="form-control">
41-
<%= f.label :subject, t('subject'), class: 'label' %>
42-
<%= f.text_field :subject, value: local_assigns[:submitter_email_message]&.subject.presence || template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
43+
<div class="flex justify-between">
44+
<%= f.label :subject, t('subject'), class: 'label' %>
45+
<% if template.submitters.size > 1 && template.submitters.size < 5 && local_assigns[:message_per_submitter] != false %>
46+
<label for="request_email_per_submitter" class="label underline">
47+
<%= t('edit_per_party') %>
48+
</label>
49+
<% end %>
50+
</div>
51+
<%= f.text_field :subject, value: local_assigns[:submitter_email_message]&.subject.presence || submitter_preferences_index.dig(local_assigns[:submitter]&.uuid, 'request_email_subject').presence || template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
4352
</div>
4453
<div class="form-control">
4554
<div class="flex items-center">
@@ -49,7 +58,7 @@
4958
</span>
5059
</div>
5160
<autoresize-textarea>
52-
<%= f.text_area :body, value: local_assigns[:submitter_email_message]&.body.presence || template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
61+
<%= f.text_area :body, value: local_assigns[:submitter_email_message]&.body.presence || submitter_preferences_index.dig(local_assigns[:submitter]&.uuid, 'request_email_body').presence || template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
5362
</autoresize-textarea>
5463
<% unless local_assigns.fetch(:disable_save_as_default_template_option, false) %>
5564
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
@@ -60,5 +69,43 @@
6069
</div>
6170
<%= render 'submissions/message_fields' %>
6271
</div>
72+
<% if template.submitters.size > 1 && template.submitters.size < 5 && local_assigns[:message_per_submitter] != false %>
73+
<div class="hidden peer-checked:block form-control space-y-2">
74+
<% options = template.submitters.map { |e| [e['name'], "request_email_#{e['uuid']}"] } %>
75+
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="flex relative px-1">
76+
<ul class="tabs w-full flex flex-nowrap">
77+
<% options.each_with_index do |(label, val), index| %>
78+
<div class="w-full">
79+
<%= f.radio_button :selected, val, checked: index.zero?, id: "#{val}_radio", data: { action: 'click:toggle-visible#trigger' }, class: 'hidden peer' %>
80+
<%= f.label :selected, label, value: val, for: "#{val}_radio", class: 'tab w-full tab-lifted peer-checked:tab-active !bg-transparent' %>
81+
</div>
82+
<% end %>
83+
</ul>
84+
</toggle-visible>
85+
<% template.submitters.each_with_index do |submitter, index| %>
86+
<%= fields_for :submitter_preferences, nil, index: submitter['uuid'] do |ff| %>
87+
<div id="request_email_<%= submitter['uuid'] %>" class="<%= 'hidden' if index != 0 %>">
88+
<div class="form-control">
89+
<div class="flex justify-between">
90+
<%= ff.label :subject, t('subject'), class: 'label' %>
91+
</div>
92+
<%= ff.text_field :subject, value: local_assigns[:submitter_email_message]&.subject.presence || submitter_preferences_index.dig(submitter['uuid'], 'request_email_subject').presence || template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
93+
</div>
94+
<div class="form-control">
95+
<div class="flex items-center">
96+
<%= ff.label :message, t('body'), class: 'label' %>
97+
<span class="tooltip tooltip-right" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY].call['body'].scan(/{.*?}/).join(', ') %>">
98+
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
99+
</span>
100+
</div>
101+
<autoresize-textarea>
102+
<%= ff.text_area :body, value: local_assigns[:submitter_email_message]&.body.presence || submitter_preferences_index.dig(submitter['uuid'], 'request_email_body').presence || template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
103+
</autoresize-textarea>
104+
</div>
105+
</div>
106+
<% end %>
107+
<% end %>
108+
</div>
109+
<% end %>
63110
</div>
64111
</div>

app/views/submissions/_value.html.erb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<% align = field.dig('preferences', 'align') %>
22
<% color = field.dig('preferences', 'color') %>
3-
<field-value dir="auto" class="flex absolute text-[1.6vw] lg:text-base <%= 'font-serif' if field.dig('preferences', 'font') == 'Times' %> <%= align == 'right' ? 'justify-end' : (align == 'center' ? 'justify-center' : '') %>" style="<%= "color: #{color}; " if color.present? %>width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%; <%= "font-size: clamp(4pt, 1.6vw, #{field['preferences']['font_size'].to_i * 1.23}pt); line-height: `clamp(6pt, 2.0vw, #{(field['preferences']['font_size'].to_i * 1.23) + 3}pt)`" if field.dig('preferences', 'font_size') %>">
3+
<% font = field.dig('preferences', 'font') %>
4+
<field-value dir="auto" class="flex absolute text-[1.6vw] lg:text-base <%= 'font-mono' if font == 'Courier' %> <%= 'font-serif' if font == 'Times' %> <%= align == 'right' ? 'justify-end' : (align == 'center' ? 'justify-center' : '') %>" style="<%= "color: #{color}; " if color.present? %>width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%; <%= "font-size: clamp(4pt, 1.6vw, #{field['preferences']['font_size'].to_i * 1.23}pt); line-height: `clamp(6pt, 2.0vw, #{(field['preferences']['font_size'].to_i * 1.23) + 3}pt)`" if field.dig('preferences', 'font_size') %>">
45
<% if field['type'] == 'signature' %>
56
<div class="flex flex-col justify-between h-full overflow-hidden">
67
<div class="flex-grow flex overflow-hidden" style="min-height: 50%">

app/views/submissions/show.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@
232232
<% elsif field['type'] == 'date' %>
233233
<%= TimeUtils.format_date_string(value, field.dig('preferences', 'format'), @submission.account.locale) %>
234234
<% else %>
235-
<%= Array.wrap(value).join(', ') %>
235+
<div class="whitespace-pre-wrap"><%= Array.wrap(value).join(', ') %></div>
236236
<% end %>
237237
</div>
238238
</div>

app/views/submissions_filters/_applied_filters.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<% query_params = params.permit(:q).merge(filter_params) %>
2-
<% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit' }[params[:status]] %>
2+
<% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit', 'sent' => 'send', 'opened' => 'mail_opened' }[params[:status]] %>
33
<div class="flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700">
44
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
55
<%= svg_icon(icon, class: 'w-5 h-5 shrink-0') %>

app/views/submissions_filters/status.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="flex flex-col md:flex-row gap-2 mt-5">
33
<div class="form-control w-full">
44
<div id="status" class="radio-select grid grid-cols-2 gap-2 px-1">
5-
<% ['', 'pending', 'completed', 'partially_completed', 'declined', 'expired'].each do |status| %>
5+
<% ['', 'pending', 'completed', 'partially_completed', 'sent', 'opened', 'declined', 'expired'].each do |status| %>
66
<label class="radio-label cursor-pointer inline-flex items-center space-x-2">
77
<%= radio_button_tag 'status', status, params[:status] == status || (status == '' && params[:status].blank?), class: 'base-radio' %>
88
<span><%= t(status.presence || 'all') %></span>

app/views/submissions_preview/completed.html.erb

+3-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
<div class="max-w-md mx-auto px-2 mt-12 mb-4">
33
<div class="space-y-6 mx-auto">
44
<div class="space-y-6">
5-
<% if Docuseal.multitenant? %>
6-
<div class="flex items-center justify-center">
7-
<%= render 'start_form/docuseal_logo' %>
8-
</div>
9-
<% end %>
5+
<div class="flex items-center justify-center">
6+
<%= render 'start_form/banner' %>
7+
</div>
108
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
119
<div class="flex items-center">
1210
<div class="mr-3">

app/views/submitters/edit.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</submitter-item>
1818
</div>
1919
<div>
20-
<%= render 'submissions/send_email', f:, template: @submitter.template, submitter: @submitter, resend_email: @submitter.sent_at?, submitter_email_message: @submitter_email_message, disable_save_as_default_template_option: true %>
20+
<%= render 'submissions/send_email', f:, template: @submitter.template, submitter: @submitter, resend_email: @submitter.sent_at?, submitter_email_message: @submitter_email_message, disable_save_as_default_template_option: true, message_per_submitter: false %>
2121
<%= render 'submissions/send_sms', f:, resend_sms: @submitter.sent_at? %>
2222
</div>
2323
<div class="form-control mt-4">

0 commit comments

Comments
 (0)