mirror of
https://github.com/docusealco/docuseal
synced 2026-11-05 14:42:33 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d86b16de6b | ||
|
|
d41710f507 | ||
|
|
2b53166763 | ||
|
|
4e56a16a58 | ||
|
|
ac7cc77017 | ||
|
|
9194879c43 | ||
|
|
1902cdaa55 | ||
|
|
6314987e69 | ||
|
|
65bcbd7737 | ||
|
|
9b25b8538e | ||
|
|
bbf8bb2a94 | ||
|
|
2085227cd9 | ||
|
|
93e0730210 | ||
|
|
25c5c11ab3 | ||
|
|
6ed5b5d35d | ||
|
|
673cc1e0df | ||
|
|
065dcf6f9f | ||
|
|
a66b16ce1c | ||
|
|
e4c0b30284 | ||
|
|
3cb37748e9 | ||
|
|
e54d76f0e2 | ||
|
|
f19e037011 | ||
|
|
ee3f47d918 | ||
|
|
a49a645022 | ||
|
|
cf30441ac6 | ||
|
|
c9bcf866ab | ||
|
|
506609f3cd | ||
|
|
721172c980 | ||
|
|
b183db5b80 | ||
|
|
c7f277f404 | ||
|
|
8ac03a9f8a | ||
|
|
dc1c386d7e | ||
|
|
1eaf4e5bab | ||
|
|
3c0f1c2e73 | ||
|
|
b2790776ef | ||
|
|
2840359396 | ||
|
|
c18ddb99a8 | ||
|
|
432435aa23 | ||
|
|
90c1a6f4ad | ||
|
|
0e8f478c2e | ||
|
|
5f23d5a5ab | ||
|
|
76821a4c2d | ||
|
|
e4a8cca90a | ||
|
|
6024ed4cd1 | ||
|
|
3811f8bbb8 | ||
|
|
306218e5df | ||
|
|
cabfe45608 | ||
|
|
4ade6a5911 | ||
|
|
10d45c3e20 | ||
|
|
6fb247ec06 | ||
|
|
1546258529 | ||
|
|
7ec83b8af6 | ||
|
|
21d0a87c17 | ||
|
|
528f59c1bf | ||
|
|
d1535c4cd8 |
No files matched your search
+9
-9
@@ -144,7 +144,7 @@ GEM
|
|||||||
cldr-plurals-runtime-rb (1.1.0)
|
cldr-plurals-runtime-rb (1.1.0)
|
||||||
cmdparse (3.0.7)
|
cmdparse (3.0.7)
|
||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
concurrent-ruby (1.3.6)
|
concurrent-ruby (1.3.7)
|
||||||
connection_pool (3.0.2)
|
connection_pool (3.0.2)
|
||||||
crack (1.0.1)
|
crack (1.0.1)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
@@ -195,13 +195,13 @@ GEM
|
|||||||
railties (>= 6.1.0)
|
railties (>= 6.1.0)
|
||||||
faker (3.6.1)
|
faker (3.6.1)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (2.14.2)
|
faraday (2.14.3)
|
||||||
faraday-net_http (>= 2.0, < 3.5)
|
faraday-net_http (>= 2.0, < 3.5)
|
||||||
json
|
json
|
||||||
logger
|
logger
|
||||||
faraday-follow_redirects (0.5.0)
|
faraday-follow_redirects (0.5.0)
|
||||||
faraday (>= 1, < 3)
|
faraday (>= 1, < 3)
|
||||||
faraday-net_http (3.4.2)
|
faraday-net_http (3.4.4)
|
||||||
net-http (~> 0.5)
|
net-http (~> 0.5)
|
||||||
ferrum (0.17.2)
|
ferrum (0.17.2)
|
||||||
addressable (~> 2.5)
|
addressable (~> 2.5)
|
||||||
@@ -271,7 +271,7 @@ GEM
|
|||||||
rdoc (>= 4.0.0)
|
rdoc (>= 4.0.0)
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.19.8)
|
json (2.19.9)
|
||||||
jwt (3.2.0)
|
jwt (3.2.0)
|
||||||
base64
|
base64
|
||||||
language_server-protocol (3.17.0.5)
|
language_server-protocol (3.17.0.5)
|
||||||
@@ -323,15 +323,15 @@ GEM
|
|||||||
net-smtp (0.5.1)
|
net-smtp (0.5.1)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.5)
|
nio4r (2.7.5)
|
||||||
nokogiri (1.19.3-aarch64-linux-gnu)
|
nokogiri (1.19.4-aarch64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.3-aarch64-linux-musl)
|
nokogiri (1.19.4-aarch64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.3-arm64-darwin)
|
nokogiri (1.19.4-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.3-x86_64-linux-gnu)
|
nokogiri (1.19.4-x86_64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.3-x86_64-linux-musl)
|
nokogiri (1.19.4-x86_64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
numo-narray-alt (0.10.3)
|
numo-narray-alt (0.10.3)
|
||||||
onnxruntime (0.10.1-aarch64-linux)
|
onnxruntime (0.10.1-aarch64-linux)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ module Api
|
|||||||
(@submission.schema_documents || @submission.template.schema_documents).size > 1
|
(@submission.schema_documents || @submission.template.schema_documents).size > 1
|
||||||
|
|
||||||
documents =
|
documents =
|
||||||
if @submission.submitters.all?(&:completed_at?)
|
if @submission.completed_at?
|
||||||
build_completed_documents(@submission, merge: is_merge)
|
build_completed_documents(@submission, merge: is_merge)
|
||||||
else
|
else
|
||||||
build_preview_documents(@submission, merge: is_merge)
|
build_preview_documents(@submission, merge: is_merge)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module Api
|
|||||||
load_and_authorize_resource :submission, parent: false
|
load_and_authorize_resource :submission, parent: false
|
||||||
|
|
||||||
def index
|
def index
|
||||||
submissions = build_completed_query(@submissions)
|
submissions = @submissions.active.where.not(completed_at: nil)
|
||||||
|
|
||||||
params[:after] = Time.zone.at(params[:after].to_i) if params[:after].present?
|
params[:after] = Time.zone.at(params[:after].to_i) if params[:after].present?
|
||||||
params[:before] = Time.zone.at(params[:before].to_i) if params[:before].present?
|
params[:before] = Time.zone.at(params[:before].to_i) if params[:before].present?
|
||||||
@@ -36,20 +36,5 @@ module Api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def build_completed_query(submissions)
|
|
||||||
submissions = submissions.where(
|
|
||||||
Submitter.where(completed_at: nil).where(
|
|
||||||
Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
|
||||||
).select(1).arel.exists.not
|
|
||||||
)
|
|
||||||
|
|
||||||
submissions.joins(:submitters)
|
|
||||||
.group(:id)
|
|
||||||
.select(Submission.arel_table[Arel.star],
|
|
||||||
Submitter.arel_table[:completed_at].maximum.as('completed_at'))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
module Api
|
module Api
|
||||||
class SubmissionsController < ApiBaseController
|
class SubmissionsController < ApiBaseController
|
||||||
|
SUBMISSION_COLUMNS = %i[id name slug source submitters_order expire_at completed_at created_at updated_at
|
||||||
|
archived_at variables template_id template_submitters created_by_user_id].freeze
|
||||||
|
TEMPLATE_COLUMNS = %i[id name external_id created_at updated_at folder_id submitters].freeze
|
||||||
|
|
||||||
load_and_authorize_resource :template, only: :create
|
load_and_authorize_resource :template, only: :create
|
||||||
load_and_authorize_resource :submission, only: %i[show index destroy]
|
load_and_authorize_resource :submission, only: %i[show index destroy]
|
||||||
|
|
||||||
@@ -9,14 +13,28 @@ module Api
|
|||||||
authorize!(:create, Submission)
|
authorize!(:create, Submission)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before_action :maybe_return_template_error, only: :create
|
||||||
|
|
||||||
def index
|
def index
|
||||||
submissions = Submissions.search(current_user, @submissions, params[:q])
|
submissions = Submissions.search(current_user, @submissions, params[:q])
|
||||||
submissions = filter_submissions(submissions, params)
|
submissions = filter_submissions(submissions, params)
|
||||||
|
|
||||||
submissions = paginate(submissions.preload(:created_by_user, :submitters,
|
with_fields = params[:include].to_s.include?('fields') || params[:include].to_s.include?('combined_document_url')
|
||||||
template: { folder: :parent_folder },
|
|
||||||
combined_document_attachment: :blob,
|
submissions = paginate(
|
||||||
audit_trail_attachment: :blob))
|
submissions.select(with_fields ? nil : SUBMISSION_COLUMNS)
|
||||||
|
.preload(:created_by_user, :submitters, combined_document_attachment: :blob,
|
||||||
|
audit_trail_attachment: :blob)
|
||||||
|
)
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(
|
||||||
|
records: submissions,
|
||||||
|
associations: :template,
|
||||||
|
scope: with_fields ? nil : Template.select(TEMPLATE_COLUMNS)
|
||||||
|
).call
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(records: submissions.filter_map(&:template),
|
||||||
|
associations: { folder: :parent_folder }).call
|
||||||
|
|
||||||
expires_at = Accounts.link_expires_at(current_account)
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
@@ -42,7 +60,7 @@ module Api
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if @submission.audit_trail_attachment.blank? && submitters.all?(&:completed_at?)
|
if @submission.audit_trail_attachment.blank? && @submission.completed_at?
|
||||||
@submission.audit_trail_attachment = Submissions::EnsureAuditGenerated.call(@submission)
|
@submission.audit_trail_attachment = Submissions::EnsureAuditGenerated.call(@submission)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -52,20 +70,6 @@ module Api
|
|||||||
def create
|
def create
|
||||||
Params::SubmissionCreateValidator.call(params)
|
Params::SubmissionCreateValidator.call(params)
|
||||||
|
|
||||||
return render json: { error: 'Template not found' }, status: :unprocessable_content if @template.nil?
|
|
||||||
|
|
||||||
if @template.archived_at?
|
|
||||||
Rollbar.warning("Archived template submission: #{@template.id}") if defined?(Rollbar)
|
|
||||||
|
|
||||||
return render json: { error: 'Template has been archived' }, status: :unprocessable_content
|
|
||||||
end
|
|
||||||
|
|
||||||
if @template.fields.blank?
|
|
||||||
Rollbar.warning("Template does not contain fields: #{@template.id}") if defined?(Rollbar)
|
|
||||||
|
|
||||||
return render json: { error: 'Template does not contain fields' }, status: :unprocessable_content
|
|
||||||
end
|
|
||||||
|
|
||||||
params[:send_email] = true unless params.key?(:send_email)
|
params[:send_email] = true unless params.key?(:send_email)
|
||||||
params[:send_sms] = false unless params.key?(:send_sms)
|
params[:send_sms] = false unless params.key?(:send_sms)
|
||||||
|
|
||||||
@@ -76,10 +80,16 @@ module Api
|
|||||||
Submissions.send_signature_requests(submissions)
|
Submissions.send_signature_requests(submissions)
|
||||||
|
|
||||||
submissions.each do |submission|
|
submissions.each do |submission|
|
||||||
|
if submission.submitters.all?(&:completed_at?) && Submissions.maybe_update_completed_at(submission)
|
||||||
|
last_submitter = submission.submitters.max_by(&:completed_at)
|
||||||
|
end
|
||||||
|
|
||||||
submission.submitters.each do |submitter|
|
submission.submitters.each do |submitter|
|
||||||
next unless submitter.completed_at?
|
next unless submitter.completed_at?
|
||||||
|
|
||||||
ProcessSubmitterCompletionJob.perform_async('submitter_id' => submitter.id, 'send_invitation_email' => false)
|
ProcessSubmitterCompletionJob.perform_async('submitter_id' => submitter.id,
|
||||||
|
'is_last' => submitter == last_submitter,
|
||||||
|
'send_invitation_email' => false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -107,6 +117,22 @@ module Api
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def maybe_return_template_error
|
||||||
|
return render json: { error: 'Template not found' }, status: :unprocessable_content if @template.nil?
|
||||||
|
|
||||||
|
if @template.archived_at?
|
||||||
|
Rollbar.warning("Archived template submission: #{@template.id}") if defined?(Rollbar)
|
||||||
|
|
||||||
|
return render json: { error: 'Template has been archived' }, status: :unprocessable_content
|
||||||
|
end
|
||||||
|
|
||||||
|
return if @template.fields.present?
|
||||||
|
|
||||||
|
Rollbar.warning("Template does not contain fields: #{@template.id}") if defined?(Rollbar)
|
||||||
|
|
||||||
|
render json: { error: 'Template does not contain fields' }, status: :unprocessable_content
|
||||||
|
end
|
||||||
|
|
||||||
def filter_submissions(submissions, params)
|
def filter_submissions(submissions, params)
|
||||||
submissions = submissions.where(template_id: params[:template_id]) if params[:template_id].present?
|
submissions = submissions.where(template_id: params[:template_id]) if params[:template_id].present?
|
||||||
submissions = submissions.where(slug: params[:slug]) if params[:slug].present?
|
submissions = submissions.where(slug: params[:slug]) if params[:slug].present?
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ module Api
|
|||||||
class SubmittersController < ApiBaseController
|
class SubmittersController < ApiBaseController
|
||||||
load_and_authorize_resource :submitter
|
load_and_authorize_resource :submitter
|
||||||
|
|
||||||
|
before_action :maybe_return_submitter_error, only: :update
|
||||||
|
|
||||||
def index
|
def index
|
||||||
submitters = Submitters.search(current_user, @submitters, params[:q])
|
submitters = Submitters.search(current_user, @submitters, params[:q])
|
||||||
|
|
||||||
@@ -36,14 +38,6 @@ module Api
|
|||||||
|
|
||||||
# rubocop:disable Metrics/MethodLength
|
# rubocop:disable Metrics/MethodLength
|
||||||
def update
|
def update
|
||||||
if @submitter.completed_at?
|
|
||||||
return render json: { error: 'Submitter has already completed the submission.' }, status: :unprocessable_content
|
|
||||||
end
|
|
||||||
|
|
||||||
if @submitter.declined_at?
|
|
||||||
return render json: { error: 'Submitter has already declined the submission.' }, status: :unprocessable_content
|
|
||||||
end
|
|
||||||
|
|
||||||
submission = @submitter.submission
|
submission = @submitter.submission
|
||||||
role = submission.template_submitters.find { |e| e['uuid'] == @submitter.uuid }['name']
|
role = submission.template_submitters.find { |e| e['uuid'] == @submitter.uuid }['name']
|
||||||
|
|
||||||
@@ -73,7 +67,9 @@ module Api
|
|||||||
end
|
end
|
||||||
|
|
||||||
if @submitter.completed_at?
|
if @submitter.completed_at?
|
||||||
ProcessSubmitterCompletionJob.perform_async('submitter_id' => @submitter.id)
|
is_last = Submissions.maybe_update_completed_at(@submitter.submission)
|
||||||
|
|
||||||
|
ProcessSubmitterCompletionJob.perform_async('submitter_id' => @submitter.id, 'is_last' => is_last)
|
||||||
elsif normalized_params[:send_email] || normalized_params[:send_sms]
|
elsif normalized_params[:send_email] || normalized_params[:send_sms]
|
||||||
Submitters.send_signature_requests([@submitter])
|
Submitters.send_signature_requests([@submitter])
|
||||||
end
|
end
|
||||||
@@ -104,7 +100,17 @@ module Api
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def maybe_filder_by_completed_at(submitters, params)
|
def maybe_return_submitter_error
|
||||||
|
if @submitter.completed_at?
|
||||||
|
return render json: { error: 'Submitter has already completed the submission.' }, status: :unprocessable_content
|
||||||
|
end
|
||||||
|
|
||||||
|
return unless @submitter.declined_at?
|
||||||
|
|
||||||
|
render json: { error: 'Submitter has already declined the submission.' }, status: :unprocessable_content
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_filter_by_completed_at(submitters, params)
|
||||||
if params[:completed_after].present?
|
if params[:completed_after].present?
|
||||||
submitters = submitters.where(completed_at: Time.zone.parse(params[:completed_after])..)
|
submitters = submitters.where(completed_at: Time.zone.parse(params[:completed_after])..)
|
||||||
end
|
end
|
||||||
@@ -177,7 +183,7 @@ module Api
|
|||||||
submitters = submitters.joins(:submission).where(submissions: { template_id: params[:template_id] })
|
submitters = submitters.joins(:submission).where(submissions: { template_id: params[:template_id] })
|
||||||
end
|
end
|
||||||
|
|
||||||
maybe_filder_by_completed_at(submitters, params)
|
maybe_filter_by_completed_at(submitters, params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_external_id(submitter, attrs)
|
def assign_external_id(submitter, attrs)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ module Api
|
|||||||
|
|
||||||
archived = params.key?(:archived) ? params[:archived] : params.dig(:template, :archived)
|
archived = params.key?(:archived) ? params[:archived] : params.dig(:template, :archived)
|
||||||
|
|
||||||
if archived.in?([true, false])
|
if archived.in?([true, false]) && current_ability.can?(:destroy, @template)
|
||||||
@template.archived_at = archived == true ? Time.current : nil
|
@template.archived_at = archived == true ? Time.current : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -57,7 +57,10 @@ module Api
|
|||||||
SearchEntries.enqueue_reindex(@template)
|
SearchEntries.enqueue_reindex(@template)
|
||||||
|
|
||||||
WebhookUrls.enqueue_events(@template, 'template.updated')
|
WebhookUrls.enqueue_events(@template, 'template.updated')
|
||||||
WebhookUrls.enqueue_events(@template, 'template.archived') if archived == true
|
|
||||||
|
if @template.saved_change_to_archived_at? && @template.archived_at?
|
||||||
|
WebhookUrls.enqueue_events(@template, 'template.archived')
|
||||||
|
end
|
||||||
|
|
||||||
render json: @template.as_json(only: %i[id updated_at])
|
render json: @template.as_json(only: %i[id updated_at])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ class PreviewDocumentPageController < ActionController::API
|
|||||||
preview_image = attachment.preview_images.joins(:blob)
|
preview_image = attachment.preview_images.joins(:blob)
|
||||||
.find_by(blob: { filename: ["#{params[:id]}.png", "#{params[:id]}.jpg"] })
|
.find_by(blob: { filename: ["#{params[:id]}.png", "#{params[:id]}.jpg"] })
|
||||||
|
|
||||||
return redirect_to preview_image.url, allow_other_host: true if preview_image
|
if preview_image
|
||||||
|
return redirect_to preview_image.url(time: ActiveStorage::Attachment.service_url_time),
|
||||||
|
allow_other_host: true
|
||||||
|
end
|
||||||
|
|
||||||
file_path =
|
file_path =
|
||||||
if attachment.service.name == :disk
|
if attachment.service.name == :disk
|
||||||
@@ -37,7 +40,7 @@ class PreviewDocumentPageController < ActionController::API
|
|||||||
preview_image =
|
preview_image =
|
||||||
Templates::ProcessDocument.generate_pdf_preview_from_file(attachment, file_path, params[:id].to_i)
|
Templates::ProcessDocument.generate_pdf_preview_from_file(attachment, file_path, params[:id].to_i)
|
||||||
|
|
||||||
redirect_to preview_image.url, allow_other_host: true
|
redirect_to preview_image.url(time: ActiveStorage::Attachment.service_url_time), allow_other_host: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_or_create_document_tempfile_path(attachment)
|
def find_or_create_document_tempfile_path(attachment)
|
||||||
|
|||||||
@@ -108,12 +108,16 @@ class StartFormController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_resubmit?(submitter)
|
def can_resubmit?(submitter)
|
||||||
|
submitter.completed_at? && submitter.completed_at > 14.days.ago &&
|
||||||
|
%w[api embed mcp].exclude?(submitter.submission.source) &&
|
||||||
submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false
|
submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_start!
|
def authorize_start!
|
||||||
return redirect_to submit_form_path(@resubmit_submitter.slug) if @resubmit_submitter && @template.archived_at?
|
is_archived = @template.archived_at? || @template.account.archived_at?
|
||||||
return redirect_to start_form_path(@template.slug) if @template.archived_at?
|
|
||||||
|
return redirect_to submit_form_path(@resubmit_submitter.slug) if @resubmit_submitter && is_archived
|
||||||
|
return redirect_to start_form_path(@template.slug) if is_archived
|
||||||
|
|
||||||
return if @resubmit_submitter
|
return if @resubmit_submitter
|
||||||
return if @template.shared_link? || (current_user && current_ability.can?(:read, @template))
|
return if @template.shared_link? || (current_user && current_ability.can?(:read, @template))
|
||||||
|
|||||||
@@ -7,17 +7,27 @@ class SubmissionsArchivedController < ApplicationController
|
|||||||
@submissions = @submissions.left_joins(:template)
|
@submissions = @submissions.left_joins(:template)
|
||||||
@submissions = @submissions.where.not(archived_at: nil)
|
@submissions = @submissions.where.not(archived_at: nil)
|
||||||
.or(@submissions.where.not(templates: { archived_at: nil }))
|
.or(@submissions.where.not(templates: { archived_at: nil }))
|
||||||
.preload(:template_accesses, :created_by_user, template: :author)
|
.preload(:template_accesses, :created_by_user)
|
||||||
|
|
||||||
@submissions = Submissions.search(current_user, @submissions, params[:q], search_template: true)
|
@submissions = Submissions.search(current_user, @submissions, params[:q], search_template: true)
|
||||||
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
||||||
|
|
||||||
@submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
@submissions =
|
||||||
@submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
if params[:status] == 'completed' || params[:completed_at_from].present? || params[:completed_at_to].present?
|
||||||
|
@submissions.order(completed_at: :desc)
|
||||||
else
|
else
|
||||||
@submissions.order(id: :desc)
|
@submissions.order(id: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
@pagy, @submissions = pagy_auto(@submissions.preload(submitters: :start_form_submission_events))
|
@pagy, @submissions = pagy_auto(@submissions.select_for_list.preload(submitters: :start_form_submission_events))
|
||||||
|
|
||||||
|
template_scope = @submissions.all?(&:template_submitters) ? Template.select_for_list : nil
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(records: @submissions,
|
||||||
|
associations: :template,
|
||||||
|
scope: template_scope).call
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(records: @submissions.filter_map(&:template),
|
||||||
|
associations: :author).call
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class SubmissionsController < ApplicationController
|
class SubmissionsController < ApplicationController
|
||||||
before_action :load_template, only: %i[new create]
|
load_and_authorize_resource :template, only: %i[new create]
|
||||||
authorize_resource :template, only: %i[new create]
|
|
||||||
|
|
||||||
load_and_authorize_resource :submission, only: %i[show destroy]
|
load_and_authorize_resource :submission, only: %i[show destroy]
|
||||||
|
|
||||||
prepend_before_action :maybe_redirect_com, only: %i[show]
|
prepend_before_action :maybe_redirect_com, only: %i[show]
|
||||||
@@ -23,7 +21,7 @@ class SubmissionsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
@submission = Submissions.preload_with_pages(@submission)
|
@submission = Submissions.preload_with_pages(@submission)
|
||||||
|
|
||||||
unless @submission.submitters.all?(&:completed_at?)
|
unless @submission.completed_at?
|
||||||
ActiveRecord::Associations::Preloader.new(
|
ActiveRecord::Associations::Preloader.new(
|
||||||
records: [@submission],
|
records: [@submission],
|
||||||
associations: [{ submitters: :start_form_submission_events }]
|
associations: [{ submitters: :start_form_submission_events }]
|
||||||
@@ -113,8 +111,4 @@ class SubmissionsController < ApplicationController
|
|||||||
def submissions_params
|
def submissions_params
|
||||||
params.permit(submission: { submitters: [:uuid, :email, :phone, :name, { values: {} }] })
|
params.permit(submission: { submitters: [:uuid, :email, :phone, :name, { values: {} }] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_template
|
|
||||||
@template = Template.accessible_by(current_ability).find(params[:template_id])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
@@ -8,17 +8,27 @@ class SubmissionsDashboardController < ApplicationController
|
|||||||
|
|
||||||
@submissions = @submissions.where(archived_at: nil)
|
@submissions = @submissions.where(archived_at: nil)
|
||||||
.where(templates: { archived_at: nil })
|
.where(templates: { archived_at: nil })
|
||||||
.preload(:template_accesses, :created_by_user, template: :author)
|
.preload(:template_accesses, :created_by_user)
|
||||||
|
|
||||||
@submissions = Submissions.search(current_user, @submissions, params[:q], search_template: true)
|
@submissions = Submissions.search(current_user, @submissions, params[:q], search_template: true)
|
||||||
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
||||||
|
|
||||||
@submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
@submissions =
|
||||||
@submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
if params[:status] == 'completed' || params[:completed_at_from].present? || params[:completed_at_to].present?
|
||||||
|
@submissions.order(completed_at: :desc)
|
||||||
else
|
else
|
||||||
@submissions.order(id: :desc)
|
@submissions.order(id: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
@pagy, @submissions = pagy_auto(@submissions.preload(submitters: :start_form_submission_events))
|
@pagy, @submissions = pagy_auto(@submissions.select_for_list.preload(submitters: :start_form_submission_events))
|
||||||
|
|
||||||
|
template_scope = @submissions.all?(&:template_submitters) ? Template.select_for_list : nil
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(records: @submissions,
|
||||||
|
associations: :template,
|
||||||
|
scope: template_scope).call
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(records: @submissions.filter_map(&:template),
|
||||||
|
associations: :author).call
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -25,7 +25,7 @@ class SubmissionsPreviewController < ApplicationController
|
|||||||
|
|
||||||
raise ActionController::RoutingError, I18n.t('not_found') if @submission.account.archived_at?
|
raise ActionController::RoutingError, I18n.t('not_found') if @submission.account.archived_at?
|
||||||
|
|
||||||
if !@submission.submitters.all?(&:completed_at?) && !signature_valid &&
|
if !@submission.completed_at? && !signature_valid &&
|
||||||
(!current_user || !current_ability.can?(:read, @submission))
|
(!current_user || !current_ability.can?(:read, @submission))
|
||||||
raise ActionController::RoutingError, I18n.t('not_found')
|
raise ActionController::RoutingError, I18n.t('not_found')
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class SubmissionsUnarchiveController < ApplicationController
|
|||||||
load_and_authorize_resource :submission
|
load_and_authorize_resource :submission
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize!(:update, @submission)
|
authorize!(:destroy, @submission)
|
||||||
|
|
||||||
@submission.update!(archived_at: nil)
|
@submission.update!(archived_at: nil)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ class SubmitFormDeclineController < ApplicationController
|
|||||||
before_action :load_submitter
|
before_action :load_submitter
|
||||||
|
|
||||||
def create
|
def create
|
||||||
return redirect_to submit_form_path(@submitter.slug) if @submitter.declined_at? ||
|
return redirect_to submit_form_path(@submitter.slug) if declining_disabled? ||
|
||||||
|
@submitter.declined_at? ||
|
||||||
@submitter.completed_at? ||
|
@submitter.completed_at? ||
|
||||||
@submitter.submission.archived_at? ||
|
@submitter.submission.archived_at? ||
|
||||||
@submitter.submission.expired? ||
|
@submitter.submission.expired? ||
|
||||||
@@ -35,6 +36,10 @@ class SubmitFormDeclineController < ApplicationController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def declining_disabled?
|
||||||
|
@submitter.account.account_configs.find_by(key: AccountConfig::ALLOW_TO_DECLINE_KEY)&.value == false
|
||||||
|
end
|
||||||
|
|
||||||
def load_submitter
|
def load_submitter
|
||||||
@submitter = Submitter.find_by!(slug: params[:submit_form_slug])
|
@submitter = Submitter.find_by!(slug: params[:submit_form_slug])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class SubmittersController < ApplicationController
|
|||||||
def update
|
def update
|
||||||
submission = @submitter.submission
|
submission = @submitter.submission
|
||||||
|
|
||||||
if @submitter.submission_events.exists?(event_type: 'start_form') || submission.archived_at? || submission.expired?
|
unless submitter_editable?(submission)
|
||||||
return redirect_back fallback_location: submission_path(submission), alert: I18n.t('submitter_cannot_be_updated')
|
return redirect_back fallback_location: submission_path(submission), alert: I18n.t('submitter_cannot_be_updated')
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -48,6 +48,12 @@ class SubmittersController < ApplicationController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def submitter_editable?(submission)
|
||||||
|
!@submitter.submission_events.exists?(event_type: 'start_form') &&
|
||||||
|
!@submitter.completed_at? && !@submitter.declined_at? &&
|
||||||
|
!submission.archived_at? && !submission.expired? && !submission.template&.archived_at?
|
||||||
|
end
|
||||||
|
|
||||||
def maybe_resend_email_sms(submitter, params)
|
def maybe_resend_email_sms(submitter, params)
|
||||||
if params[:send_email] == '1' && submitter.email.present?
|
if params[:send_email] == '1' && submitter.email.present?
|
||||||
is_sent_recently = Docuseal.multitenant? &&
|
is_sent_recently = Docuseal.multitenant? &&
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ class SubmittersResubmitController < ApplicationController
|
|||||||
|
|
||||||
def update
|
def update
|
||||||
return redirect_to submit_form_path(slug: @submitter.slug) if @submitter.email != current_user.email
|
return redirect_to submit_form_path(slug: @submitter.slug) if @submitter.email != current_user.email
|
||||||
|
return redirect_to submit_form_path(slug: @submitter.slug) if @submitter.completed_at.blank? ||
|
||||||
|
@submitter.completed_at < 1.month.ago
|
||||||
|
|
||||||
submission = @submitter.account.submissions.new(created_by_user: current_user,
|
submission = @submitter.account.submissions.new(created_by_user: current_user,
|
||||||
submitters_order: :preserved,
|
submitters_order: :preserved,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class TemplateDocumentsModifyController < ApplicationController
|
|||||||
params.require(:documents).map do |item|
|
params.require(:documents).map do |item|
|
||||||
item.permit(:attachment_uuid,
|
item.permit(:attachment_uuid,
|
||||||
pages: [:attachment_uuid, :page, :rotate,
|
pages: [:attachment_uuid, :page, :rotate,
|
||||||
{ redact: [%i[x y w h]], replaced_page: %i[attachment_uuid page] }]).to_h
|
{ redact: [%i[x y w h color]], replaced_page: %i[attachment_uuid page] }]).to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
Templates::ModifyDocuments.call(@template, documents_layout)
|
Templates::ModifyDocuments.call(@template, documents_layout)
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ class TemplateFoldersController < ApplicationController
|
|||||||
(@template_folders.size < 7 ? 9 : 6)
|
(@template_folders.size < 7 ? 9 : 6)
|
||||||
end
|
end
|
||||||
|
|
||||||
@pagy, @templates = pagy_auto(@templates, limit:)
|
@pagy, @templates = pagy_auto(@templates.select_for_list, limit:)
|
||||||
|
|
||||||
load_related_submissions if params[:q].present? && @templates.blank?
|
if params[:q].present? && @templates.blank?
|
||||||
|
@related_submissions_pagy, @related_submissions = load_related_submissions(@template_folder)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
@pagy, @template_folders = pagy(@template_folders, limit: FOLDERS_PER_PAGE)
|
@pagy, @template_folders = pagy(@template_folders, limit: FOLDERS_PER_PAGE)
|
||||||
|
|
||||||
@@ -55,11 +57,10 @@ class TemplateFoldersController < ApplicationController
|
|||||||
|
|
||||||
def selected_order
|
def selected_order
|
||||||
@selected_order ||=
|
@selected_order ||=
|
||||||
if cookies.permanent[:dashboard_templates_order].blank? ||
|
if can?(:manage, :countless)
|
||||||
(cookies.permanent[:dashboard_templates_order] == 'used_at' && can?(:manage, :countless))
|
|
||||||
'created_at'
|
'created_at'
|
||||||
else
|
else
|
||||||
cookies.permanent[:dashboard_templates_order]
|
cookies.permanent[:dashboard_templates_order].presence || 'created_at'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -67,20 +68,20 @@ class TemplateFoldersController < ApplicationController
|
|||||||
params.require(:template_folder).permit(:name)
|
params.require(:template_folder).permit(:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_related_submissions
|
def load_related_submissions(template_folder)
|
||||||
@related_submissions =
|
related_submissions =
|
||||||
Submission.accessible_by(current_ability)
|
Submission.accessible_by(current_ability)
|
||||||
.where(archived_at: nil)
|
.where(archived_at: nil)
|
||||||
.where(template_id: current_account.templates.active
|
.where(template_id: current_account.templates.active
|
||||||
.where(folder: [@template_folder, *@template_folder.subfolders])
|
.where(folder: [template_folder, *template_folder.subfolders])
|
||||||
.select(:id))
|
.select(:id))
|
||||||
.preload(:template_accesses, :created_by_user,
|
.preload(:template_accesses, :created_by_user,
|
||||||
template: :author,
|
template: :author,
|
||||||
submitters: :start_form_submission_events)
|
submitters: :start_form_submission_events)
|
||||||
|
|
||||||
@related_submissions = Submissions.search(current_user, @related_submissions, params[:q])
|
related_submissions = Submissions.search(current_user, related_submissions, params[:q])
|
||||||
.order(id: :desc)
|
.order(id: :desc)
|
||||||
|
|
||||||
@related_submissions_pagy, @related_submissions = pagy_auto(@related_submissions, limit: 5)
|
pagy_auto(related_submissions.select_for_list, limit: 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -10,11 +10,17 @@ class TemplatesArchivedController < ApplicationController
|
|||||||
|
|
||||||
@templates = Templates.search(current_user, @templates, params[:q])
|
@templates = Templates.search(current_user, @templates, params[:q])
|
||||||
|
|
||||||
@pagy, @templates = pagy_auto(@templates, limit: 12)
|
@pagy, @templates = pagy_auto(@templates.select_for_list, limit: 12)
|
||||||
|
|
||||||
return unless params[:q].present? && @templates.blank?
|
return unless params[:q].present? && @templates.blank?
|
||||||
|
|
||||||
@related_submissions =
|
@related_submissions_pagy, @related_submissions = load_related_submissions
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_related_submissions
|
||||||
|
related_submissions =
|
||||||
Submission.accessible_by(current_ability)
|
Submission.accessible_by(current_ability)
|
||||||
.joins(:template)
|
.joins(:template)
|
||||||
.where.not(templates: { archived_at: nil })
|
.where.not(templates: { archived_at: nil })
|
||||||
@@ -22,9 +28,9 @@ class TemplatesArchivedController < ApplicationController
|
|||||||
template: :author,
|
template: :author,
|
||||||
submitters: :start_form_submission_events)
|
submitters: :start_form_submission_events)
|
||||||
|
|
||||||
@related_submissions = Submissions.search(current_user, @related_submissions, params[:q])
|
related_submissions = Submissions.search(current_user, related_submissions, params[:q])
|
||||||
.order(id: :desc)
|
.order(id: :desc)
|
||||||
|
|
||||||
@related_submissions_pagy, @related_submissions = pagy_auto(@related_submissions, limit: 5)
|
pagy_auto(related_submissions.select_for_list, limit: 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -10,12 +10,12 @@ class TemplatesArchivedSubmissionsController < ApplicationController
|
|||||||
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
||||||
|
|
||||||
@submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
@submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
||||||
@submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
@submissions.order(completed_at: :desc)
|
||||||
else
|
else
|
||||||
@submissions.order(id: :desc)
|
@submissions.order(id: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
@pagy, @submissions = pagy_auto(@submissions.preload(submitters: :start_form_submission_events))
|
@pagy, @submissions = pagy_auto(@submissions.select_for_list.preload(submitters: :start_form_submission_events))
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ class TemplatesController < ApplicationController
|
|||||||
submissions = Submissions::Filter.filter_by_status(submissions, params)
|
submissions = Submissions::Filter.filter_by_status(submissions, params)
|
||||||
|
|
||||||
submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
||||||
submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
submissions.order(completed_at: :desc)
|
||||||
else
|
else
|
||||||
submissions.order(id: :desc)
|
submissions.order(id: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
@pagy, @submissions = pagy_auto(submissions.preload(:template_accesses, submitters: :start_form_submission_events))
|
@pagy, @submissions =
|
||||||
|
pagy_auto(submissions.select_for_list.preload(:template_accesses, submitters: :start_form_submission_events))
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,76 +11,116 @@ class TemplatesDashboardController < ApplicationController
|
|||||||
helper_method :selected_order
|
helper_method :selected_order
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@default_folder = current_account.default_template_folder
|
||||||
|
|
||||||
@template_folders =
|
@template_folders =
|
||||||
TemplateFolders.filter_active_folders(@template_folders.where(parent_folder_id: nil), @templates)
|
TemplateFolders.filter_active_folders(@template_folders.where(parent_folder_id: nil), @templates)
|
||||||
|
|
||||||
|
@template_folders = @template_folders.where.not(id: @default_folder.id) if params[:q].blank?
|
||||||
|
|
||||||
@template_folders = TemplateFolders.search(@template_folders, params[:q])
|
@template_folders = TemplateFolders.search(@template_folders, params[:q])
|
||||||
@template_folders = TemplateFolders.sort(@template_folders, current_user, selected_order)
|
@template_folders = TemplateFolders.sort(@template_folders, current_user, selected_order)
|
||||||
|
|
||||||
@pagy, @template_folders = pagy(
|
@shared_templates = Templates.shared(current_user).active
|
||||||
@template_folders,
|
|
||||||
limit: FOLDERS_PER_PAGE,
|
@pagy, @template_folders, @show_default_folder, @show_shared_folder, @show_shared_inline =
|
||||||
page: @template_folders.count > SHOW_TEMPLATES_FOLDERS_THRESHOLD ? params[:page] : 1
|
load_folders(@template_folders, @templates, @shared_templates)
|
||||||
)
|
|
||||||
|
|
||||||
if @pagy.count > SHOW_TEMPLATES_FOLDERS_THRESHOLD
|
if @pagy.count > SHOW_TEMPLATES_FOLDERS_THRESHOLD
|
||||||
@templates = @templates.none
|
@templates = @templates.none
|
||||||
else
|
else
|
||||||
@template_folders = @template_folders.reject { |e| e.name == TemplateFolder::DEFAULT_NAME }
|
if @show_shared_inline
|
||||||
@templates = filter_templates(@templates).preload(:author, :template_accesses)
|
@templates = @shared_templates.preload(:template_sharings)
|
||||||
@templates = Templates::Order.call(@templates, current_user, selected_order)
|
|
||||||
|
|
||||||
limit =
|
|
||||||
if @template_folders.size < 4
|
|
||||||
TEMPLATES_PER_PAGE
|
|
||||||
else
|
else
|
||||||
(@template_folders.size < 7 ? 9 : 6)
|
@templates = @templates.active
|
||||||
|
@templates = @templates.where(folder_id: @default_folder.id) if params[:q].blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
@pagy, @templates = pagy_auto(@templates, limit:)
|
@pagy, @templates = load_templates(@templates.select_for_list, @pagy.count,
|
||||||
|
show_shared_inline: @show_shared_inline)
|
||||||
|
|
||||||
load_related_submissions if params[:q].present? && @templates.blank?
|
if params[:q].present? && @templates.blank?
|
||||||
|
@related_submissions_pagy, @related_submissions = load_related_submissions
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def filter_templates(templates)
|
def load_templates(templates, folders_count, show_shared_inline: false)
|
||||||
rel = templates.active
|
templates = templates.preload(:author, :template_accesses)
|
||||||
|
|
||||||
if params[:q].blank?
|
templates =
|
||||||
if Docuseal.multitenant? ? current_account.testing? : current_account.linked_account_account
|
if show_shared_inline
|
||||||
shared_account_ids = [current_user.account_id]
|
Templates.search_shared(current_user, templates, params[:q])
|
||||||
shared_account_ids << TemplateSharing::ALL_ID if !Docuseal.multitenant? && !current_account.testing?
|
|
||||||
|
|
||||||
shared_template_ids = TemplateSharing.where(account_id: shared_account_ids).select(:template_id)
|
|
||||||
|
|
||||||
rel = Template.where(
|
|
||||||
Template.arel_table[:id].in(
|
|
||||||
rel.where(folder_id: current_account.default_template_folder.id).select(:id).arel
|
|
||||||
.union(:all, shared_template_ids.arel)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
else
|
||||||
rel = rel.where(folder_id: current_account.default_template_folder.id)
|
Templates.search(current_user, templates, params[:q])
|
||||||
|
end
|
||||||
|
|
||||||
|
templates = Templates::Order.call(templates, current_user, selected_order)
|
||||||
|
|
||||||
|
limit =
|
||||||
|
if folders_count < 4
|
||||||
|
TEMPLATES_PER_PAGE
|
||||||
|
else
|
||||||
|
(folders_count < 7 ? 9 : 6)
|
||||||
|
end
|
||||||
|
|
||||||
|
pagy_auto(templates, limit:)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_folders(template_folders, templates, shared_templates)
|
||||||
|
if params[:q].present?
|
||||||
|
pagy(template_folders, limit: FOLDERS_PER_PAGE,
|
||||||
|
page: template_folders.count > SHOW_TEMPLATES_FOLDERS_THRESHOLD ? params[:page] : 1)
|
||||||
|
else
|
||||||
|
load_folders_with_pinned(template_folders, templates, shared_templates)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Templates.search(current_user, rel, params[:q])
|
def load_folders_with_pinned(template_folders, templates, shared_templates)
|
||||||
|
folders_count = template_folders.count
|
||||||
|
|
||||||
|
shared_exists = shared_templates.exists?
|
||||||
|
default_has_templates = templates.active.exists?(folder_id: current_account.default_template_folder.id)
|
||||||
|
|
||||||
|
show_inline_folders =
|
||||||
|
folders_count + (shared_exists && default_has_templates ? 1 : 0) <= SHOW_TEMPLATES_FOLDERS_THRESHOLD
|
||||||
|
|
||||||
|
show_shared_inline = shared_exists && !default_has_templates && show_inline_folders
|
||||||
|
|
||||||
|
show_shared_in_grid = shared_exists && !show_shared_inline
|
||||||
|
show_default_in_grid = !show_inline_folders && default_has_templates
|
||||||
|
|
||||||
|
pinned_count = (show_default_in_grid ? 1 : 0) + (show_shared_in_grid ? 1 : 0)
|
||||||
|
|
||||||
|
pagy = Pagy::Offset.new(count: folders_count + pinned_count,
|
||||||
|
page: show_inline_folders ? 1 : [params[:page].to_s.to_i, 1].max,
|
||||||
|
limit: FOLDERS_PER_PAGE,
|
||||||
|
raise_range_error: true)
|
||||||
|
|
||||||
|
show_default_folder = show_default_in_grid && pagy.page == 1
|
||||||
|
show_shared_folder = show_shared_in_grid && pagy.page == 1
|
||||||
|
|
||||||
|
folder_offset = pagy.page == 1 ? 0 : pagy.offset - pinned_count
|
||||||
|
folder_limit = pagy.page == 1 ? FOLDERS_PER_PAGE - pinned_count : FOLDERS_PER_PAGE
|
||||||
|
|
||||||
|
template_folders = template_folders.offset(folder_offset).limit(folder_limit)
|
||||||
|
|
||||||
|
[pagy, template_folders, show_default_folder, show_shared_folder, show_shared_inline]
|
||||||
end
|
end
|
||||||
|
|
||||||
def selected_order
|
def selected_order
|
||||||
@selected_order ||=
|
@selected_order ||=
|
||||||
if cookies.permanent[:dashboard_templates_order].blank? ||
|
if can?(:manage, :countless)
|
||||||
(cookies.permanent[:dashboard_templates_order] == 'used_at' && can?(:manage, :countless))
|
|
||||||
'created_at'
|
'created_at'
|
||||||
else
|
else
|
||||||
cookies.permanent[:dashboard_templates_order]
|
cookies.permanent[:dashboard_templates_order].presence || 'created_at'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_related_submissions
|
def load_related_submissions
|
||||||
@related_submissions = Submission.accessible_by(current_ability)
|
related_submissions = Submission.accessible_by(current_ability)
|
||||||
.left_joins(:template)
|
.left_joins(:template)
|
||||||
.where(archived_at: nil)
|
.where(archived_at: nil)
|
||||||
.where(templates: { archived_at: nil })
|
.where(templates: { archived_at: nil })
|
||||||
@@ -88,9 +128,9 @@ class TemplatesDashboardController < ApplicationController
|
|||||||
template: :author,
|
template: :author,
|
||||||
submitters: :start_form_submission_events)
|
submitters: :start_form_submission_events)
|
||||||
|
|
||||||
@related_submissions = Submissions.search(current_user, @related_submissions, params[:q])
|
related_submissions = Submissions.search(current_user, related_submissions, params[:q])
|
||||||
.order(id: :desc)
|
.order(id: :desc)
|
||||||
|
|
||||||
@related_submissions_pagy, @related_submissions = pagy_auto(@related_submissions, limit: 5)
|
pagy_auto(related_submissions.select_for_list, limit: 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4,7 +4,7 @@ class TemplatesRestoreController < ApplicationController
|
|||||||
load_and_authorize_resource :template
|
load_and_authorize_resource :template
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize!(:update, @template)
|
authorize!(:destroy, @template)
|
||||||
|
|
||||||
@template.update!(archived_at: nil)
|
@template.update!(archived_at: nil)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class TemplatesSharedController < ApplicationController
|
||||||
|
def index
|
||||||
|
authorize!(:read, Template)
|
||||||
|
|
||||||
|
@is_archived = params[:archived] == 'true'
|
||||||
|
|
||||||
|
@templates = Templates.shared(current_user)
|
||||||
|
|
||||||
|
@has_archived = !@is_archived && @templates.archived.exists?
|
||||||
|
|
||||||
|
@templates = @is_archived ? @templates.archived : @templates.active
|
||||||
|
|
||||||
|
@templates = @templates.preload(:author, :template_accesses, :template_sharings)
|
||||||
|
.order(id: :desc)
|
||||||
|
|
||||||
|
@templates = Templates.search_shared(current_user, @templates, params[:q])
|
||||||
|
|
||||||
|
@pagy, @templates = pagy_auto(@templates.select_for_list, limit: 12)
|
||||||
|
|
||||||
|
return unless params[:q].present? && @templates.blank?
|
||||||
|
|
||||||
|
@related_submissions_pagy, @related_submissions = load_related_submissions(is_archived: @is_archived)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_related_submissions(is_archived:)
|
||||||
|
shared_templates = Templates.shared(current_user)
|
||||||
|
shared_templates = is_archived ? shared_templates.archived : shared_templates.active
|
||||||
|
|
||||||
|
related_submissions =
|
||||||
|
Submission.accessible_by(current_ability)
|
||||||
|
.where(template_id: shared_templates.select(:id))
|
||||||
|
.preload(:template_accesses, :created_by_user,
|
||||||
|
template: :author,
|
||||||
|
submitters: :start_form_submission_events)
|
||||||
|
|
||||||
|
related_submissions = related_submissions.where(archived_at: nil) unless is_archived
|
||||||
|
|
||||||
|
related_submissions = Submissions.search(current_user, related_submissions, params[:q])
|
||||||
|
.order(id: :desc)
|
||||||
|
|
||||||
|
pagy_auto(related_submissions.select_for_list, limit: 5)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -289,6 +289,7 @@
|
|||||||
:id="currentField.uuid"
|
:id="currentField.uuid"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
:required="currentField.required"
|
:required="currentField.required"
|
||||||
|
:aria-label="showFieldNames && (currentField.name || currentField.title) ? undefined : (currentField.name || currentField.title || t('select_your_option'))"
|
||||||
:aria-describedby="currentField.description ? currentField.uuid + '-desc' : undefined"
|
:aria-describedby="currentField.description ? currentField.uuid + '-desc' : undefined"
|
||||||
class="select base-input !text-2xl w-full text-center font-normal"
|
class="select base-input !text-2xl w-full text-center font-normal"
|
||||||
:class="{ 'text-gray-300': !values[currentField.uuid] }"
|
:class="{ 'text-gray-300': !values[currentField.uuid] }"
|
||||||
@@ -317,7 +318,7 @@
|
|||||||
<div v-else-if="currentField.type === 'radio'">
|
<div v-else-if="currentField.type === 'radio'">
|
||||||
<label
|
<label
|
||||||
v-if="showFieldNames && (currentField.name || currentField.title)"
|
v-if="showFieldNames && (currentField.name || currentField.title)"
|
||||||
:for="currentField.uuid"
|
:id="currentField.uuid + '-radio-label'"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !currentField.description }"
|
:class="{ 'mb-2': !currentField.description }"
|
||||||
@@ -356,6 +357,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="space-y-3.5 mx-auto"
|
class="space-y-3.5 mx-auto"
|
||||||
|
role="radiogroup"
|
||||||
|
:aria-labelledby="(currentField.name || currentField.title) ? currentField.uuid + '-radio-label' : null"
|
||||||
:class="{ hidden: !showFieldNames || (currentField.options.every((e) => !e.value) && currentField.options.length > 4) }"
|
:class="{ hidden: !showFieldNames || (currentField.options.every((e) => !e.value) && currentField.options.length > 4) }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const en = {
|
|||||||
close: 'Close',
|
close: 'Close',
|
||||||
uploaded_files: 'Uploaded files',
|
uploaded_files: 'Uploaded files',
|
||||||
signature_drawing_area: 'Signature drawing area. Use mouse or touch to draw your signature.',
|
signature_drawing_area: 'Signature drawing area. Use mouse or touch to draw your signature.',
|
||||||
|
initials_drawing_area: 'Initials drawing area. Use mouse or touch to draw your initials.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Please upload an image file',
|
please_upload_an_image_file: 'Please upload an image file',
|
||||||
must_be_characters_length: 'Must be {number} characters long',
|
must_be_characters_length: 'Must be {number} characters long',
|
||||||
@@ -88,6 +89,7 @@ const en = {
|
|||||||
please_check_the_box_to_continue: 'Please check the box to continue.',
|
please_check_the_box_to_continue: 'Please check the box to continue.',
|
||||||
open_source_documents_software: 'open source documents software',
|
open_source_documents_software: 'open source documents software',
|
||||||
verified_phone_number: 'Verify Phone Number',
|
verified_phone_number: 'Verify Phone Number',
|
||||||
|
country_code: 'Country code',
|
||||||
use_international_format: 'Use international format: +1xxx',
|
use_international_format: 'Use international format: +1xxx',
|
||||||
six_digits_code: '6-digit code',
|
six_digits_code: '6-digit code',
|
||||||
change_phone_number: 'Change phone number',
|
change_phone_number: 'Change phone number',
|
||||||
@@ -120,6 +122,7 @@ const es = {
|
|||||||
close: 'Cerrar',
|
close: 'Cerrar',
|
||||||
uploaded_files: 'Archivos subidos',
|
uploaded_files: 'Archivos subidos',
|
||||||
signature_drawing_area: 'Área de dibujo de firma. Use el ratón o toque para dibujar su firma.',
|
signature_drawing_area: 'Área de dibujo de firma. Use el ratón o toque para dibujar su firma.',
|
||||||
|
initials_drawing_area: 'Área de dibujo de iniciales. Use el ratón o toque para dibujar sus iniciales.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Por favor, sube un archivo de imagen',
|
please_upload_an_image_file: 'Por favor, sube un archivo de imagen',
|
||||||
must_be_characters_length: 'Debe tener {number} caracteres de longitud',
|
must_be_characters_length: 'Debe tener {number} caracteres de longitud',
|
||||||
@@ -204,6 +207,7 @@ const es = {
|
|||||||
please_check_the_box_to_continue: 'Por favor marque la casilla para continuar.',
|
please_check_the_box_to_continue: 'Por favor marque la casilla para continuar.',
|
||||||
open_source_documents_software: 'software de documentos de código abierto',
|
open_source_documents_software: 'software de documentos de código abierto',
|
||||||
verified_phone_number: 'Verificar número de teléfono',
|
verified_phone_number: 'Verificar número de teléfono',
|
||||||
|
country_code: 'Código de país',
|
||||||
use_international_format: 'Usar formato internacional: +1xxx',
|
use_international_format: 'Usar formato internacional: +1xxx',
|
||||||
six_digits_code: 'Código de 6 dígitos',
|
six_digits_code: 'Código de 6 dígitos',
|
||||||
change_phone_number: 'Cambiar número de teléfono',
|
change_phone_number: 'Cambiar número de teléfono',
|
||||||
@@ -236,6 +240,7 @@ const it = {
|
|||||||
close: 'Chiudi',
|
close: 'Chiudi',
|
||||||
uploaded_files: 'File caricati',
|
uploaded_files: 'File caricati',
|
||||||
signature_drawing_area: 'Area di disegno della firma. Usa il mouse o il tocco per disegnare la tua firma.',
|
signature_drawing_area: 'Area di disegno della firma. Usa il mouse o il tocco per disegnare la tua firma.',
|
||||||
|
initials_drawing_area: 'Area di disegno delle iniziali. Usa il mouse o il tocco per disegnare le tue iniziali.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Per favore carica un file immagine',
|
please_upload_an_image_file: 'Per favore carica un file immagine',
|
||||||
must_be_characters_length: 'Deve essere lungo {number} caratteri',
|
must_be_characters_length: 'Deve essere lungo {number} caratteri',
|
||||||
@@ -320,6 +325,7 @@ const it = {
|
|||||||
please_check_the_box_to_continue: 'Si prega di spuntare la casella per continuare.',
|
please_check_the_box_to_continue: 'Si prega di spuntare la casella per continuare.',
|
||||||
open_source_documents_software: 'software di documenti open source',
|
open_source_documents_software: 'software di documenti open source',
|
||||||
verified_phone_number: 'Verifica numero di telefono',
|
verified_phone_number: 'Verifica numero di telefono',
|
||||||
|
country_code: 'Prefisso internazionale',
|
||||||
use_international_format: 'Usa formato internazionale: +1xxx',
|
use_international_format: 'Usa formato internazionale: +1xxx',
|
||||||
six_digits_code: 'Codice a 6 cifre',
|
six_digits_code: 'Codice a 6 cifre',
|
||||||
change_phone_number: 'Cambia numero di telefono',
|
change_phone_number: 'Cambia numero di telefono',
|
||||||
@@ -352,6 +358,7 @@ const de = {
|
|||||||
close: 'Schließen',
|
close: 'Schließen',
|
||||||
uploaded_files: 'Hochgeladene Dateien',
|
uploaded_files: 'Hochgeladene Dateien',
|
||||||
signature_drawing_area: 'Unterschriftszeichenbereich. Verwenden Sie die Maus oder Berührung, um Ihre Unterschrift zu zeichnen.',
|
signature_drawing_area: 'Unterschriftszeichenbereich. Verwenden Sie die Maus oder Berührung, um Ihre Unterschrift zu zeichnen.',
|
||||||
|
initials_drawing_area: 'Zeichenbereich für Initialen. Verwenden Sie die Maus oder Berührung, um Ihre Initialen zu zeichnen.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Bitte laden Sie eine Bilddatei hoch',
|
please_upload_an_image_file: 'Bitte laden Sie eine Bilddatei hoch',
|
||||||
must_be_characters_length: 'Muss {number} Zeichen lang sein',
|
must_be_characters_length: 'Muss {number} Zeichen lang sein',
|
||||||
@@ -436,6 +443,7 @@ const de = {
|
|||||||
please_check_the_box_to_continue: 'Bitte aktivieren Sie das Kontrollkästchen, um fortzufahren.',
|
please_check_the_box_to_continue: 'Bitte aktivieren Sie das Kontrollkästchen, um fortzufahren.',
|
||||||
open_source_documents_software: 'Open-Source-Dokumentensoftware',
|
open_source_documents_software: 'Open-Source-Dokumentensoftware',
|
||||||
verified_phone_number: 'Telefonnummer verifizieren',
|
verified_phone_number: 'Telefonnummer verifizieren',
|
||||||
|
country_code: 'Ländervorwahl',
|
||||||
use_international_format: 'Internationales Format verwenden: +1xxx',
|
use_international_format: 'Internationales Format verwenden: +1xxx',
|
||||||
six_digits_code: '6-stelliger Code',
|
six_digits_code: '6-stelliger Code',
|
||||||
change_phone_number: 'Telefonnummer ändern',
|
change_phone_number: 'Telefonnummer ändern',
|
||||||
@@ -468,6 +476,7 @@ const fr = {
|
|||||||
close: 'Fermer',
|
close: 'Fermer',
|
||||||
uploaded_files: 'Fichiers téléchargés',
|
uploaded_files: 'Fichiers téléchargés',
|
||||||
signature_drawing_area: 'Zone de dessin de signature. Utilisez la souris ou le toucher pour dessiner votre signature.',
|
signature_drawing_area: 'Zone de dessin de signature. Utilisez la souris ou le toucher pour dessiner votre signature.',
|
||||||
|
initials_drawing_area: 'Zone de dessin des initiales. Utilisez la souris ou le toucher pour dessiner vos initiales.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Veuillez téléverser un fichier image',
|
please_upload_an_image_file: 'Veuillez téléverser un fichier image',
|
||||||
must_be_characters_length: 'Doit comporter {number} caractères',
|
must_be_characters_length: 'Doit comporter {number} caractères',
|
||||||
@@ -552,6 +561,7 @@ const fr = {
|
|||||||
please_check_the_box_to_continue: 'Veuillez cocher la case pour continuer.',
|
please_check_the_box_to_continue: 'Veuillez cocher la case pour continuer.',
|
||||||
open_source_documents_software: 'logiciel de documents open source',
|
open_source_documents_software: 'logiciel de documents open source',
|
||||||
verified_phone_number: 'Vérifier le numéro de téléphone',
|
verified_phone_number: 'Vérifier le numéro de téléphone',
|
||||||
|
country_code: 'Indicatif du pays',
|
||||||
use_international_format: 'Utilisez le format international : +1xxx',
|
use_international_format: 'Utilisez le format international : +1xxx',
|
||||||
six_digits_code: 'Code à 6 chiffres',
|
six_digits_code: 'Code à 6 chiffres',
|
||||||
change_phone_number: 'Changer de numéro de téléphone',
|
change_phone_number: 'Changer de numéro de téléphone',
|
||||||
@@ -584,6 +594,7 @@ const pl = {
|
|||||||
close: 'Zamknij',
|
close: 'Zamknij',
|
||||||
uploaded_files: 'Przesłane pliki',
|
uploaded_files: 'Przesłane pliki',
|
||||||
signature_drawing_area: 'Obszar rysowania podpisu. Użyj myszy lub dotyku, aby narysować swój podpis.',
|
signature_drawing_area: 'Obszar rysowania podpisu. Użyj myszy lub dotyku, aby narysować swój podpis.',
|
||||||
|
initials_drawing_area: 'Obszar rysowania inicjałów. Użyj myszy lub dotyku, aby narysować swoje inicjały.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Proszę przesłać plik obrazu',
|
please_upload_an_image_file: 'Proszę przesłać plik obrazu',
|
||||||
must_be_characters_length: 'Musi mieć długość {number} znaków',
|
must_be_characters_length: 'Musi mieć długość {number} znaków',
|
||||||
@@ -668,6 +679,7 @@ const pl = {
|
|||||||
please_check_the_box_to_continue: 'Proszę zaznaczyć pole, aby kontynuować.',
|
please_check_the_box_to_continue: 'Proszę zaznaczyć pole, aby kontynuować.',
|
||||||
open_source_documents_software: 'oprogramowanie do dokumentów open source',
|
open_source_documents_software: 'oprogramowanie do dokumentów open source',
|
||||||
verified_phone_number: 'Zweryfikuj numer telefonu',
|
verified_phone_number: 'Zweryfikuj numer telefonu',
|
||||||
|
country_code: 'Kod kraju',
|
||||||
use_international_format: 'Użyj międzynarodowego formatu: +1xxx',
|
use_international_format: 'Użyj międzynarodowego formatu: +1xxx',
|
||||||
six_digits_code: '6-cyfrowy kod',
|
six_digits_code: '6-cyfrowy kod',
|
||||||
change_phone_number: 'Zmień numer telefonu',
|
change_phone_number: 'Zmień numer telefonu',
|
||||||
@@ -700,6 +712,7 @@ const uk = {
|
|||||||
close: 'Закрити',
|
close: 'Закрити',
|
||||||
uploaded_files: 'Завантажені файли',
|
uploaded_files: 'Завантажені файли',
|
||||||
signature_drawing_area: 'Область малювання підпису. Використовуйте мишу або дотик, щоб намалювати свій підпис.',
|
signature_drawing_area: 'Область малювання підпису. Використовуйте мишу або дотик, щоб намалювати свій підпис.',
|
||||||
|
initials_drawing_area: 'Область малювання ініціалів. Використовуйте мишу або дотик, щоб намалювати свої ініціали.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Будь ласка, завантажте файл зображення',
|
please_upload_an_image_file: 'Будь ласка, завантажте файл зображення',
|
||||||
must_be_characters_length: 'Має містити {number} символів',
|
must_be_characters_length: 'Має містити {number} символів',
|
||||||
@@ -784,6 +797,7 @@ const uk = {
|
|||||||
please_check_the_box_to_continue: 'Будь ласка, позначте прапорець, щоб продовжити.',
|
please_check_the_box_to_continue: 'Будь ласка, позначте прапорець, щоб продовжити.',
|
||||||
open_source_documents_software: 'відкрите програмне забезпечення для документів',
|
open_source_documents_software: 'відкрите програмне забезпечення для документів',
|
||||||
verified_phone_number: 'Підтвердіть номер телефону',
|
verified_phone_number: 'Підтвердіть номер телефону',
|
||||||
|
country_code: 'Код країни',
|
||||||
use_international_format: 'Використовуйте міжнародний формат: +1xxx',
|
use_international_format: 'Використовуйте міжнародний формат: +1xxx',
|
||||||
six_digits_code: '6-значний код',
|
six_digits_code: '6-значний код',
|
||||||
change_phone_number: 'Змінити номер телефону',
|
change_phone_number: 'Змінити номер телефону',
|
||||||
@@ -816,6 +830,7 @@ const cs = {
|
|||||||
close: 'Zavřít',
|
close: 'Zavřít',
|
||||||
uploaded_files: 'Nahrané soubory',
|
uploaded_files: 'Nahrané soubory',
|
||||||
signature_drawing_area: 'Oblast pro kreslení podpisu. Použijte myš nebo dotyk k nakreslení podpisu.',
|
signature_drawing_area: 'Oblast pro kreslení podpisu. Použijte myš nebo dotyk k nakreslení podpisu.',
|
||||||
|
initials_drawing_area: 'Oblast pro kreslení iniciál. Použijte myš nebo dotyk k nakreslení iniciál.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Nahrajte prosím obrázkový soubor',
|
please_upload_an_image_file: 'Nahrajte prosím obrázkový soubor',
|
||||||
must_be_characters_length: 'Musí mít délku {number} znaků',
|
must_be_characters_length: 'Musí mít délku {number} znaků',
|
||||||
@@ -900,6 +915,7 @@ const cs = {
|
|||||||
please_check_the_box_to_continue: 'Prosím, zaškrtněte políčko pro pokračování.',
|
please_check_the_box_to_continue: 'Prosím, zaškrtněte políčko pro pokračování.',
|
||||||
open_source_documents_software: 'open source software pro dokumenty',
|
open_source_documents_software: 'open source software pro dokumenty',
|
||||||
verified_phone_number: 'Ověřte telefonní číslo',
|
verified_phone_number: 'Ověřte telefonní číslo',
|
||||||
|
country_code: 'Předvolba země',
|
||||||
use_international_format: 'Použijte mezinárodní formát: +1xxx',
|
use_international_format: 'Použijte mezinárodní formát: +1xxx',
|
||||||
six_digits_code: '6-místný kód',
|
six_digits_code: '6-místný kód',
|
||||||
change_phone_number: 'Změnit telefonní číslo',
|
change_phone_number: 'Změnit telefonní číslo',
|
||||||
@@ -932,6 +948,7 @@ const pt = {
|
|||||||
close: 'Fechar',
|
close: 'Fechar',
|
||||||
uploaded_files: 'Arquivos enviados',
|
uploaded_files: 'Arquivos enviados',
|
||||||
signature_drawing_area: 'Área de desenho da assinatura. Use o mouse ou toque para desenhar sua assinatura.',
|
signature_drawing_area: 'Área de desenho da assinatura. Use o mouse ou toque para desenhar sua assinatura.',
|
||||||
|
initials_drawing_area: 'Área de desenho das iniciais. Use o mouse ou toque para desenhar suas iniciais.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Por favor, envie um arquivo de imagem',
|
please_upload_an_image_file: 'Por favor, envie um arquivo de imagem',
|
||||||
must_be_characters_length: 'Deve ter {number} caracteres',
|
must_be_characters_length: 'Deve ter {number} caracteres',
|
||||||
@@ -1016,6 +1033,7 @@ const pt = {
|
|||||||
please_check_the_box_to_continue: 'Por favor, marque a caixa para continuar.',
|
please_check_the_box_to_continue: 'Por favor, marque a caixa para continuar.',
|
||||||
open_source_documents_software: 'software de documentos de código aberto',
|
open_source_documents_software: 'software de documentos de código aberto',
|
||||||
verified_phone_number: 'Verificar Número de Telefone',
|
verified_phone_number: 'Verificar Número de Telefone',
|
||||||
|
country_code: 'Código do país',
|
||||||
use_international_format: 'Use formato internacional: +1xxx',
|
use_international_format: 'Use formato internacional: +1xxx',
|
||||||
six_digits_code: 'Código de 6 dígitos',
|
six_digits_code: 'Código de 6 dígitos',
|
||||||
change_phone_number: 'Alterar número de telefone',
|
change_phone_number: 'Alterar número de telefone',
|
||||||
@@ -1048,6 +1066,7 @@ const he = {
|
|||||||
close: 'סגור',
|
close: 'סגור',
|
||||||
uploaded_files: 'קבצים שהועלו',
|
uploaded_files: 'קבצים שהועלו',
|
||||||
signature_drawing_area: 'אזור ציור חתימה. השתמש בעכבר או במגע כדי לצייר את החתימה שלך.',
|
signature_drawing_area: 'אזור ציור חתימה. השתמש בעכבר או במגע כדי לצייר את החתימה שלך.',
|
||||||
|
initials_drawing_area: 'אזור ציור ראשי תיבות. השתמש בעכבר או במגע כדי לצייר את ראשי התיבות שלך.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'אנא העלה קובץ תמונה',
|
please_upload_an_image_file: 'אנא העלה קובץ תמונה',
|
||||||
must_be_characters_length: 'חייב להיות באורך של {number} תווים',
|
must_be_characters_length: 'חייב להיות באורך של {number} תווים',
|
||||||
@@ -1132,6 +1151,7 @@ const he = {
|
|||||||
please_check_the_box_to_continue: 'אנא סמן את התיבה כדי להמשיך.',
|
please_check_the_box_to_continue: 'אנא סמן את התיבה כדי להמשיך.',
|
||||||
open_source_documents_software: 'תוכנה פתוחה למסמכים',
|
open_source_documents_software: 'תוכנה פתוחה למסמכים',
|
||||||
verified_phone_number: 'אימות מספר טלפון',
|
verified_phone_number: 'אימות מספר טלפון',
|
||||||
|
country_code: 'קידומת מדינה',
|
||||||
use_international_format: 'השתמש בפורמט בינלאומי: +1xxx',
|
use_international_format: 'השתמש בפורמט בינלאומי: +1xxx',
|
||||||
six_digits_code: 'קוד שש ספרות',
|
six_digits_code: 'קוד שש ספרות',
|
||||||
change_phone_number: 'שינוי מספר טלפון',
|
change_phone_number: 'שינוי מספר טלפון',
|
||||||
@@ -1164,6 +1184,7 @@ const nl = {
|
|||||||
close: 'Sluiten',
|
close: 'Sluiten',
|
||||||
uploaded_files: 'Geüploade bestanden',
|
uploaded_files: 'Geüploade bestanden',
|
||||||
signature_drawing_area: 'Handtekening tekengebied. Gebruik de muis of aanraking om uw handtekening te tekenen.',
|
signature_drawing_area: 'Handtekening tekengebied. Gebruik de muis of aanraking om uw handtekening te tekenen.',
|
||||||
|
initials_drawing_area: 'Tekengebied voor initialen. Gebruik de muis of aanraking om uw initialen te tekenen.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'Upload alstublieft een afbeeldingsbestand',
|
please_upload_an_image_file: 'Upload alstublieft een afbeeldingsbestand',
|
||||||
must_be_characters_length: 'Moet {number} tekens lang zijn',
|
must_be_characters_length: 'Moet {number} tekens lang zijn',
|
||||||
@@ -1248,6 +1269,7 @@ const nl = {
|
|||||||
please_check_the_box_to_continue: 'Vink het vakje aan om door te gaan.',
|
please_check_the_box_to_continue: 'Vink het vakje aan om door te gaan.',
|
||||||
open_source_documents_software: 'Open source documenten software',
|
open_source_documents_software: 'Open source documenten software',
|
||||||
verified_phone_number: 'Telefoonnummer verifiëren',
|
verified_phone_number: 'Telefoonnummer verifiëren',
|
||||||
|
country_code: 'Landcode',
|
||||||
use_international_format: 'Gebruik internationaal formaat: +1xxx',
|
use_international_format: 'Gebruik internationaal formaat: +1xxx',
|
||||||
six_digits_code: '6-cijferige code',
|
six_digits_code: '6-cijferige code',
|
||||||
change_phone_number: 'Wijzig telefoonnummer',
|
change_phone_number: 'Wijzig telefoonnummer',
|
||||||
@@ -1280,6 +1302,7 @@ const ar = {
|
|||||||
close: 'إغلاق',
|
close: 'إغلاق',
|
||||||
uploaded_files: 'الملفات المرفوعة',
|
uploaded_files: 'الملفات المرفوعة',
|
||||||
signature_drawing_area: 'منطقة رسم التوقيع. استخدم الماوس أو اللمس لرسم توقيعك.',
|
signature_drawing_area: 'منطقة رسم التوقيع. استخدم الماوس أو اللمس لرسم توقيعك.',
|
||||||
|
initials_drawing_area: 'منطقة رسم الحروف الأولى. استخدم الماوس أو اللمس لرسم الحروف الأولى.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: 'يرجى تحميل ملف صورة',
|
please_upload_an_image_file: 'يرجى تحميل ملف صورة',
|
||||||
must_be_characters_length: 'يجب أن يكون الطول {number} حرفًا',
|
must_be_characters_length: 'يجب أن يكون الطول {number} حرفًا',
|
||||||
@@ -1364,6 +1387,7 @@ const ar = {
|
|||||||
please_check_the_box_to_continue: 'الرجاء التحقق من الخانة للمتابعة.',
|
please_check_the_box_to_continue: 'الرجاء التحقق من الخانة للمتابعة.',
|
||||||
open_source_documents_software: 'برنامج وثائق مفتوح المصدر',
|
open_source_documents_software: 'برنامج وثائق مفتوح المصدر',
|
||||||
verified_phone_number: 'تحقق من رقم الهاتف',
|
verified_phone_number: 'تحقق من رقم الهاتف',
|
||||||
|
country_code: 'رمز الدولة',
|
||||||
use_international_format: 'استخدم الشكل الدولي: +1xxx',
|
use_international_format: 'استخدم الشكل الدولي: +1xxx',
|
||||||
six_digits_code: 'رمز مكون من 6 أرقام',
|
six_digits_code: 'رمز مكون من 6 أرقام',
|
||||||
change_phone_number: 'تغيير رقم الهاتف',
|
change_phone_number: 'تغيير رقم الهاتف',
|
||||||
@@ -1396,6 +1420,7 @@ const ko = {
|
|||||||
close: '닫기',
|
close: '닫기',
|
||||||
uploaded_files: '업로드된 파일',
|
uploaded_files: '업로드된 파일',
|
||||||
signature_drawing_area: '서명 그리기 영역. 마우스 또는 터치를 사용하여 서명을 그리세요.',
|
signature_drawing_area: '서명 그리기 영역. 마우스 또는 터치를 사용하여 서명을 그리세요.',
|
||||||
|
initials_drawing_area: '이니셜 그리기 영역. 마우스 또는 터치를 사용하여 이니셜을 그리세요.',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: '이미지 파일을 업로드해 주세요',
|
please_upload_an_image_file: '이미지 파일을 업로드해 주세요',
|
||||||
must_be_characters_length: '{number}자여야 합니다',
|
must_be_characters_length: '{number}자여야 합니다',
|
||||||
@@ -1480,6 +1505,7 @@ const ko = {
|
|||||||
please_check_the_box_to_continue: '계속하려면 확인란을 선택하십시오.',
|
please_check_the_box_to_continue: '계속하려면 확인란을 선택하십시오.',
|
||||||
open_source_documents_software: '오픈 소스 문서 소프트웨어',
|
open_source_documents_software: '오픈 소스 문서 소프트웨어',
|
||||||
verified_phone_number: '전화번호 확인됨',
|
verified_phone_number: '전화번호 확인됨',
|
||||||
|
country_code: '국가 번호',
|
||||||
use_international_format: '국제 포맷 사용: +1xxx',
|
use_international_format: '국제 포맷 사용: +1xxx',
|
||||||
six_digits_code: '6자리 코드',
|
six_digits_code: '6자리 코드',
|
||||||
change_phone_number: '전화번호 변경',
|
change_phone_number: '전화번호 변경',
|
||||||
@@ -1512,6 +1538,7 @@ const ja = {
|
|||||||
close: '閉じる',
|
close: '閉じる',
|
||||||
uploaded_files: 'アップロードされたファイル',
|
uploaded_files: 'アップロードされたファイル',
|
||||||
signature_drawing_area: '署名描画エリア。マウスまたはタッチを使用して署名を描いてください。',
|
signature_drawing_area: '署名描画エリア。マウスまたはタッチを使用して署名を描いてください。',
|
||||||
|
initials_drawing_area: 'イニシャル描画エリア。マウスまたはタッチを使用してイニシャルを描いてください。',
|
||||||
kba: 'KBA',
|
kba: 'KBA',
|
||||||
please_upload_an_image_file: '画像ファイルをアップロードしてください',
|
please_upload_an_image_file: '画像ファイルをアップロードしてください',
|
||||||
must_be_characters_length: '{number}文字でなければなりません',
|
must_be_characters_length: '{number}文字でなければなりません',
|
||||||
@@ -1596,6 +1623,7 @@ const ja = {
|
|||||||
please_check_the_box_to_continue: '続行するにはボックスにチェックを入れてください。',
|
please_check_the_box_to_continue: '続行するにはボックスにチェックを入れてください。',
|
||||||
open_source_documents_software: 'オープンソースのドキュメントソフトウェア',
|
open_source_documents_software: 'オープンソースのドキュメントソフトウェア',
|
||||||
verified_phone_number: '電話番号を認証',
|
verified_phone_number: '電話番号を認証',
|
||||||
|
country_code: '国番号',
|
||||||
use_international_format: '国際形式を使用してください:+1xxx',
|
use_international_format: '国際形式を使用してください:+1xxx',
|
||||||
six_digits_code: '6桁のコード',
|
six_digits_code: '6桁のコード',
|
||||||
change_phone_number: '電話番号を変更',
|
change_phone_number: '電話番号を変更',
|
||||||
|
|||||||
@@ -141,6 +141,7 @@
|
|||||||
<img
|
<img
|
||||||
v-if="modelValue || computedPreviousValue"
|
v-if="modelValue || computedPreviousValue"
|
||||||
:src="attachmentsIndex[modelValue || computedPreviousValue].url"
|
:src="attachmentsIndex[modelValue || computedPreviousValue].url"
|
||||||
|
:alt="field.name || t('initials')"
|
||||||
class="mx-auto bg-white border border-base-300 rounded max-h-44"
|
class="mx-auto bg-white border border-base-300 rounded max-h-44"
|
||||||
>
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -156,6 +157,8 @@
|
|||||||
<canvas
|
<canvas
|
||||||
v-show="!modelValue && !computedPreviousValue"
|
v-show="!modelValue && !computedPreviousValue"
|
||||||
ref="canvas"
|
ref="canvas"
|
||||||
|
role="img"
|
||||||
|
:aria-label="t('initials_drawing_area')"
|
||||||
class="bg-white border border-base-300 rounded-2xl w-full draw-canvas"
|
class="bg-white border border-base-300 rounded-2xl w-full draw-canvas"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,6 +168,7 @@
|
|||||||
ref="textInput"
|
ref="textInput"
|
||||||
class="base-input !text-2xl w-full mt-6 text-center"
|
class="base-input !text-2xl w-full mt-6 text-center"
|
||||||
:required="field.required && !isInitialsStarted"
|
:required="field.required && !isInitialsStarted"
|
||||||
|
:aria-label="field.name || t('initials')"
|
||||||
:placeholder="`${t('type_initial_here')}...`"
|
:placeholder="`${t('type_initial_here')}...`"
|
||||||
type="text"
|
type="text"
|
||||||
@focus="$emit('focus')"
|
@focus="$emit('focus')"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
:placeholder="t('email')"
|
:placeholder="t('email')"
|
||||||
type="email"
|
type="email"
|
||||||
:required="submitters.includes(submitter)"
|
:required="submitters.includes(submitter)"
|
||||||
autofocus="true"
|
:autofocus="index === 0"
|
||||||
name="submission[submitters][][email]"
|
name="submission[submitters][][email]"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
v-if="showFieldNames && (field.name || field.title)"
|
v-if="showFieldNames && (field.name || field.title)"
|
||||||
:for="field.uuid"
|
:id="field.uuid + '-group-label'"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
@@ -44,6 +44,8 @@
|
|||||||
<div
|
<div
|
||||||
class="space-y-3.5 mx-auto"
|
class="space-y-3.5 mx-auto"
|
||||||
:class="{ hidden: !showOptions }"
|
:class="{ hidden: !showOptions }"
|
||||||
|
role="group"
|
||||||
|
:aria-labelledby="showFieldNames && (field.name || field.title) ? field.uuid + '-group-label' : undefined"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(option, index) in field.options"
|
v-for="(option, index) in field.options"
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
<select
|
<select
|
||||||
id="country_code_select"
|
id="country_code_select"
|
||||||
class="absolute top-0 bottom-0 right-0 left-0 opacity-0 w-full h-full cursor-pointer"
|
class="absolute top-0 bottom-0 right-0 left-0 opacity-0 w-full h-full cursor-pointer"
|
||||||
|
:aria-label="t('country_code')"
|
||||||
:disabled="!!defaultValue"
|
:disabled="!!defaultValue"
|
||||||
@change="onCountrySelect(countries.find((country) => country.flag === $event.target.value))"
|
@change="onCountrySelect(countries.find((country) => country.flag === $event.target.value))"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
id="type_text_button"
|
id="type_text_button"
|
||||||
|
ref="typeTextButton"
|
||||||
type="button"
|
type="button"
|
||||||
:aria-label="t('draw_signature')"
|
:aria-label="t('draw_signature')"
|
||||||
class="btn btn-outline btn-sm font-medium type-text-button"
|
class="btn btn-outline btn-sm font-medium type-text-button"
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
id="type_text_button"
|
id="type_text_button"
|
||||||
|
ref="typeTextButton"
|
||||||
type="button"
|
type="button"
|
||||||
:aria-label="t('type_text')"
|
:aria-label="t('type_text')"
|
||||||
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap type-text-button"
|
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap type-text-button"
|
||||||
@@ -71,7 +73,7 @@
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
:aria-label="t('take_photo')"
|
:aria-label="t('upload')"
|
||||||
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap upload-image-button"
|
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap upload-image-button"
|
||||||
@click="$refs.takePhotoInput.click()"
|
@click="$refs.takePhotoInput.click()"
|
||||||
>
|
>
|
||||||
@@ -199,7 +201,7 @@
|
|||||||
<canvas
|
<canvas
|
||||||
v-show="!modelValue && !computedPreviousValue"
|
v-show="!modelValue && !computedPreviousValue"
|
||||||
ref="canvas"
|
ref="canvas"
|
||||||
role="application"
|
role="img"
|
||||||
:aria-label="t('signature_drawing_area')"
|
:aria-label="t('signature_drawing_area')"
|
||||||
style="padding: 1px; 0"
|
style="padding: 1px; 0"
|
||||||
class="bg-white border border-base-300 rounded-2xl w-full draw-canvas"
|
class="bg-white border border-base-300 rounded-2xl w-full draw-canvas"
|
||||||
@@ -228,6 +230,8 @@
|
|||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
ref="qrCanvas"
|
ref="qrCanvas"
|
||||||
|
role="img"
|
||||||
|
:aria-label="t('scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature')"
|
||||||
class="h-full"
|
class="h-full"
|
||||||
width="132"
|
width="132"
|
||||||
height="132"
|
height="132"
|
||||||
@@ -242,6 +246,7 @@
|
|||||||
ref="textInput"
|
ref="textInput"
|
||||||
class="base-input !text-2xl w-full mt-6"
|
class="base-input !text-2xl w-full mt-6"
|
||||||
:required="field.required && !isSignatureStarted"
|
:required="field.required && !isSignatureStarted"
|
||||||
|
:aria-label="field.name || t('signature')"
|
||||||
:placeholder="`${t('type_signature_here')}...`"
|
:placeholder="`${t('type_signature_here')}...`"
|
||||||
type="text"
|
type="text"
|
||||||
@input="updateWrittenSignature"
|
@input="updateWrittenSignature"
|
||||||
@@ -251,6 +256,7 @@
|
|||||||
class="select base-input !text-2xl w-full mt-6 text-center"
|
class="select base-input !text-2xl w-full mt-6 text-center"
|
||||||
:class="{ 'text-gray-300': !reason }"
|
:class="{ 'text-gray-300': !reason }"
|
||||||
required
|
required
|
||||||
|
:aria-label="t('select_a_reason')"
|
||||||
:name="`values[${field.preferences.reason_field_uuid}]`"
|
:name="`values[${field.preferences.reason_field_uuid}]`"
|
||||||
@change="$event.target.value === 'other' ? [reason = '', isOtherReason = true] : $emit('update:reason', $event.target.value)"
|
@change="$event.target.value === 'other' ? [reason = '', isOtherReason = true] : $emit('update:reason', $event.target.value)"
|
||||||
>
|
>
|
||||||
@@ -296,6 +302,7 @@
|
|||||||
class="base-input !text-2xl w-full mt-6"
|
class="base-input !text-2xl w-full mt-6"
|
||||||
required
|
required
|
||||||
:name="`values[${field.preferences.reason_field_uuid}]`"
|
:name="`values[${field.preferences.reason_field_uuid}]`"
|
||||||
|
:aria-label="t('select_a_reason')"
|
||||||
:placeholder="t('type_here_')"
|
:placeholder="t('type_here_')"
|
||||||
:value="reason"
|
:value="reason"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -716,10 +723,12 @@ export default {
|
|||||||
this.clear()
|
this.clear()
|
||||||
this.isTextSignature = !this.isTextSignature
|
this.isTextSignature = !this.isTextSignature
|
||||||
|
|
||||||
if (this.isTextSignature) {
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
if (this.isTextSignature) {
|
||||||
if (this.$refs.textInput) {
|
if (this.$refs.textInput) {
|
||||||
if (!this.submitter.name) {
|
if (this.submitter.name) {
|
||||||
|
this.$refs.typeTextButton?.focus()
|
||||||
|
} else {
|
||||||
this.$refs.textInput.focus()
|
this.$refs.textInput.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,8 +736,10 @@ export default {
|
|||||||
|
|
||||||
this.$emit('start')
|
this.$emit('start')
|
||||||
}
|
}
|
||||||
})
|
} else {
|
||||||
|
this.$refs.typeTextButton?.focus()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
async initTypedSignature () {
|
async initTypedSignature () {
|
||||||
if (this.signatureText) {
|
if (this.signatureText) {
|
||||||
|
|||||||
@@ -726,6 +726,10 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.inputMode && (this.isValueInput || this.isCheckboxInput || this.isSelectInput)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
document.activeElement?.blur()
|
document.activeElement?.blur()
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
class="absolute w-5 h-5 -ml-2.5 -mt-2.5 rounded-full bg-white border-2 border-neutral-600 cursor-move shadow"
|
class="absolute w-5 h-5 -ml-2.5 -mt-2.5 rounded-full bg-white border-2 border-neutral-600 cursor-move shadow"
|
||||||
:style="{ left: `${corner.x * 100}%`, top: `${corner.y * 100}%` }"
|
:style="{ left: `${corner.x * 100}%`, top: `${corner.y * 100}%` }"
|
||||||
@mousedown.prevent="onCornerMousedown(cornerIndex)"
|
@mousedown.prevent="onCornerMousedown(cornerIndex)"
|
||||||
|
@touchstart.prevent="onCornerTouchstart(cornerIndex)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -218,6 +219,9 @@ export default {
|
|||||||
beforeUnmount () {
|
beforeUnmount () {
|
||||||
window.removeEventListener('mousemove', this.onMousemove)
|
window.removeEventListener('mousemove', this.onMousemove)
|
||||||
window.removeEventListener('mouseup', this.onMouseup)
|
window.removeEventListener('mouseup', this.onMouseup)
|
||||||
|
window.removeEventListener('touchmove', this.onTouchmove)
|
||||||
|
window.removeEventListener('touchend', this.onTouchend)
|
||||||
|
window.removeEventListener('touchcancel', this.onTouchend)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
transformPoint (point, rotate, flipH, flipV) {
|
transformPoint (point, rotate, flipH, flipV) {
|
||||||
@@ -276,27 +280,48 @@ export default {
|
|||||||
y: Math.min(Math.max((event.clientY - rect.top) / rect.height, 0), 1)
|
y: Math.min(Math.max((event.clientY - rect.top) / rect.height, 0), 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCornerMousedown (index) {
|
startCornerDrag (index) {
|
||||||
this.draggingIndex = index
|
this.draggingIndex = index
|
||||||
this.cornersTouched = true
|
this.cornersTouched = true
|
||||||
|
},
|
||||||
|
dragCorner (point) {
|
||||||
|
if (this.draggingIndex === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.corners[this.draggingIndex] = this.inverseTransformPoint(this.pagePoint(point), this.rotate, this.flipH, this.flipV)
|
||||||
|
},
|
||||||
|
onCornerMousedown (index) {
|
||||||
|
this.startCornerDrag(index)
|
||||||
|
|
||||||
window.addEventListener('mousemove', this.onMousemove)
|
window.addEventListener('mousemove', this.onMousemove)
|
||||||
window.addEventListener('mouseup', this.onMouseup, { once: true })
|
window.addEventListener('mouseup', this.onMouseup, { once: true })
|
||||||
},
|
},
|
||||||
onMousemove (event) {
|
onMousemove (event) {
|
||||||
if (this.draggingIndex === null) {
|
this.dragCorner(event)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const point = this.inverseTransformPoint(this.pagePoint(event), this.rotate, this.flipH, this.flipV)
|
|
||||||
|
|
||||||
this.corners[this.draggingIndex] = point
|
|
||||||
},
|
},
|
||||||
onMouseup () {
|
onMouseup () {
|
||||||
window.removeEventListener('mousemove', this.onMousemove)
|
window.removeEventListener('mousemove', this.onMousemove)
|
||||||
|
|
||||||
this.draggingIndex = null
|
this.draggingIndex = null
|
||||||
},
|
},
|
||||||
|
onCornerTouchstart (index) {
|
||||||
|
this.startCornerDrag(index)
|
||||||
|
|
||||||
|
window.addEventListener('touchmove', this.onTouchmove, { passive: false })
|
||||||
|
window.addEventListener('touchend', this.onTouchend)
|
||||||
|
window.addEventListener('touchcancel', this.onTouchend)
|
||||||
|
},
|
||||||
|
onTouchmove (event) {
|
||||||
|
this.dragCorner(event.touches[0])
|
||||||
|
},
|
||||||
|
onTouchend () {
|
||||||
|
window.removeEventListener('touchmove', this.onTouchmove)
|
||||||
|
window.removeEventListener('touchend', this.onTouchend)
|
||||||
|
window.removeEventListener('touchcancel', this.onTouchend)
|
||||||
|
|
||||||
|
this.draggingIndex = null
|
||||||
|
},
|
||||||
submit (scan) {
|
submit (scan) {
|
||||||
this.isProcessing = scan ? 'scan' : 'crop'
|
this.isProcessing = scan ? 'scan' : 'crop'
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,8 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(rect, rectIndex) in page.redact"
|
v-for="(rect, rectIndex) in page.redact"
|
||||||
:key="`redact-${rectIndex}`"
|
:key="`redact-${rectIndex}`"
|
||||||
class="absolute bg-black"
|
class="absolute"
|
||||||
|
:class="rect.color === 'white' ? 'bg-white' : 'bg-black'"
|
||||||
:style="{
|
:style="{
|
||||||
left: `${rect.x * 100}%`,
|
left: `${rect.x * 100}%`,
|
||||||
top: `${rect.y * 100}%`,
|
top: `${rect.y * 100}%`,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
class="relative mx-auto select-none cursor-crosshair"
|
class="relative mx-auto select-none cursor-crosshair"
|
||||||
:style="pageStyle"
|
:style="pageStyle"
|
||||||
@mousedown.prevent="onMousedown"
|
@mousedown.prevent="onMousedown"
|
||||||
|
@touchstart.prevent="onTouchstart"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="imageUrl"
|
:src="imageUrl"
|
||||||
@@ -22,7 +23,8 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(rect, rectIndex) in redactRects"
|
v-for="(rect, rectIndex) in redactRects"
|
||||||
:key="`rect-${rectIndex}`"
|
:key="`rect-${rectIndex}`"
|
||||||
class="absolute bg-black pointer-events-none"
|
class="absolute pointer-events-none"
|
||||||
|
:class="color === 'white' ? 'bg-white' : 'bg-black'"
|
||||||
:style="{
|
:style="{
|
||||||
left: `${rect.x * 100}%`,
|
left: `${rect.x * 100}%`,
|
||||||
top: `${rect.y * 100}%`,
|
top: `${rect.y * 100}%`,
|
||||||
@@ -47,6 +49,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-56 flex-none border-l px-4 py-4 space-y-2">
|
<div class="w-56 flex-none border-l px-4 py-4 space-y-2">
|
||||||
|
<div class="flex items-center justify-between mb-1">
|
||||||
|
<span class="text-sm pl-1">{{ t('color') }}</span>
|
||||||
|
<div
|
||||||
|
class="join rounded"
|
||||||
|
style="height: 28px"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="option in colors"
|
||||||
|
:key="option"
|
||||||
|
class="btn btn-sm join-item !h-7 !min-h-0 bg-white input-bordered hover:border-base-content/20 hover:bg-base-100/50 px-2"
|
||||||
|
:class="{ '!bg-base-200': color === option }"
|
||||||
|
@click.prevent="color = option"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="block w-10 h-4 border border-base-content/30"
|
||||||
|
:style="{ backgroundColor: option }"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm w-full justify-start normal-case font-normal rounded disabled:bg-base-300"
|
class="btn btn-sm w-full justify-start normal-case font-normal rounded disabled:bg-base-300"
|
||||||
:disabled="!hasRedactions && !wasReset"
|
:disabled="!hasRedactions && !wasReset"
|
||||||
@@ -122,11 +144,15 @@ export default {
|
|||||||
selectedNodes: {},
|
selectedNodes: {},
|
||||||
freeRects: [],
|
freeRects: [],
|
||||||
rects: [],
|
rects: [],
|
||||||
|
color: 'black',
|
||||||
wasReset: false,
|
wasReset: false,
|
||||||
marquee: null
|
marquee: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
colors () {
|
||||||
|
return ['black', 'white']
|
||||||
|
},
|
||||||
rotate () {
|
rotate () {
|
||||||
return this.page.rotate || 0
|
return this.page.rotate || 0
|
||||||
},
|
},
|
||||||
@@ -186,6 +212,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
if ((this.page.redact || []).some((rect) => rect.color === 'white')) {
|
||||||
|
this.color = 'white'
|
||||||
|
}
|
||||||
|
|
||||||
if (this.imagePage) {
|
if (this.imagePage) {
|
||||||
this.rects = (this.page.redact || []).map((rect) => ({ ...rect }))
|
this.rects = (this.page.redact || []).map((rect) => ({ ...rect }))
|
||||||
|
|
||||||
@@ -222,6 +252,9 @@ export default {
|
|||||||
beforeUnmount () {
|
beforeUnmount () {
|
||||||
window.removeEventListener('mousemove', this.onMousemove)
|
window.removeEventListener('mousemove', this.onMousemove)
|
||||||
window.removeEventListener('mouseup', this.onMouseup)
|
window.removeEventListener('mouseup', this.onMouseup)
|
||||||
|
window.removeEventListener('touchmove', this.onTouchmove)
|
||||||
|
window.removeEventListener('touchend', this.onTouchend)
|
||||||
|
window.removeEventListener('touchcancel', this.onTouchend)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
inverseRotatePoint (point, rotate) {
|
inverseRotatePoint (point, rotate) {
|
||||||
@@ -238,7 +271,17 @@ export default {
|
|||||||
return { x, y }
|
return { x, y }
|
||||||
},
|
},
|
||||||
apply () {
|
apply () {
|
||||||
this.$emit('apply', this.redactRects)
|
const rects = this.redactRects.map((rect) => {
|
||||||
|
const next = { x: rect.x, y: rect.y, w: rect.w, h: rect.h }
|
||||||
|
|
||||||
|
if (this.color === 'white') {
|
||||||
|
next.color = 'white'
|
||||||
|
}
|
||||||
|
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit('apply', rects)
|
||||||
},
|
},
|
||||||
reset () {
|
reset () {
|
||||||
this.wasReset = true
|
this.wasReset = true
|
||||||
@@ -318,27 +361,61 @@ export default {
|
|||||||
y: Math.min(Math.max((event.clientY - rect.top) / rect.height, 0), 1)
|
y: Math.min(Math.max((event.clientY - rect.top) / rect.height, 0), 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
startMarquee (point) {
|
||||||
|
const start = this.pagePoint(point)
|
||||||
|
|
||||||
|
this.marquee = { x1: start.x, y1: start.y, x2: start.x, y2: start.y }
|
||||||
|
},
|
||||||
|
updateMarquee (point) {
|
||||||
|
if (!this.marquee) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = this.pagePoint(point)
|
||||||
|
|
||||||
|
this.marquee.x2 = next.x
|
||||||
|
this.marquee.y2 = next.y
|
||||||
|
},
|
||||||
onMousedown (event) {
|
onMousedown (event) {
|
||||||
if (event.button !== 0 || (!this.imagePage && !this.textNodes)) {
|
if (event.button !== 0 || (!this.imagePage && !this.textNodes)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const point = this.pagePoint(event)
|
this.startMarquee(event)
|
||||||
|
|
||||||
this.marquee = { x1: point.x, y1: point.y, x2: point.x, y2: point.y }
|
|
||||||
|
|
||||||
window.addEventListener('mousemove', this.onMousemove)
|
window.addEventListener('mousemove', this.onMousemove)
|
||||||
window.addEventListener('mouseup', this.onMouseup, { once: true })
|
window.addEventListener('mouseup', this.onMouseup, { once: true })
|
||||||
},
|
},
|
||||||
onMousemove (event) {
|
onMousemove (event) {
|
||||||
const point = this.pagePoint(event)
|
this.updateMarquee(event)
|
||||||
|
|
||||||
this.marquee.x2 = point.x
|
|
||||||
this.marquee.y2 = point.y
|
|
||||||
},
|
},
|
||||||
onMouseup () {
|
onMouseup () {
|
||||||
window.removeEventListener('mousemove', this.onMousemove)
|
window.removeEventListener('mousemove', this.onMousemove)
|
||||||
|
|
||||||
|
this.finishMarquee()
|
||||||
|
},
|
||||||
|
onTouchstart (event) {
|
||||||
|
if (!this.imagePage && !this.textNodes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startMarquee(event.touches[0])
|
||||||
|
|
||||||
|
window.addEventListener('touchmove', this.onTouchmove, { passive: false })
|
||||||
|
window.addEventListener('touchend', this.onTouchend)
|
||||||
|
window.addEventListener('touchcancel', this.onTouchend)
|
||||||
|
},
|
||||||
|
onTouchmove (event) {
|
||||||
|
this.updateMarquee(event.touches[0])
|
||||||
|
},
|
||||||
|
onTouchend () {
|
||||||
|
window.removeEventListener('touchmove', this.onTouchmove)
|
||||||
|
window.removeEventListener('touchend', this.onTouchend)
|
||||||
|
window.removeEventListener('touchcancel', this.onTouchend)
|
||||||
|
|
||||||
|
this.finishMarquee()
|
||||||
|
},
|
||||||
|
finishMarquee () {
|
||||||
if (!this.marquee) {
|
if (!this.marquee) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,7 +600,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
showFont () {
|
showFont () {
|
||||||
return ['text', 'number', 'date', 'select', 'heading', 'cells'].includes(this.field.type)
|
return ['text', 'number', 'date', 'select', 'heading', 'cells'].includes(this.field.type) ||
|
||||||
|
(['radio', 'multiple'].includes(this.field.type) && this.field.areas?.every((a) => !a.option_uuid))
|
||||||
},
|
},
|
||||||
showDescription () {
|
showDescription () {
|
||||||
return !['stamp', 'heading', 'strikethrough'].includes(this.field.type)
|
return !['stamp', 'heading', 'strikethrough'].includes(this.field.type)
|
||||||
|
|||||||
@@ -452,7 +452,7 @@
|
|||||||
class="pb-0.5 mt-0.5"
|
class="pb-0.5 mt-0.5"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-if="['text', 'number', 'date', 'select', 'heading', 'cells'].includes(field.type)"
|
v-if="['text', 'number', 'date', 'select', 'heading', 'cells'].includes(field.type) || (['radio', 'multiple'].includes(field.type) && field.areas?.every((a) => !a.option_uuid))"
|
||||||
class="field-settings-font"
|
class="field-settings-font"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ const en = {
|
|||||||
flip_horizontal: 'Flip horizontal',
|
flip_horizontal: 'Flip horizontal',
|
||||||
flip_vertical: 'Flip vertical',
|
flip_vertical: 'Flip vertical',
|
||||||
there_is_no_text_to_redact_on_this_page: 'This page contains only images. Redact tool can be used only with text pages',
|
there_is_no_text_to_redact_on_this_page: 'This page contains only images. Redact tool can be used only with text pages',
|
||||||
|
color: 'Color',
|
||||||
reset: 'Reset',
|
reset: 'Reset',
|
||||||
upload_to_document: 'Upload to "{document}"',
|
upload_to_document: 'Upload to "{document}"',
|
||||||
replace_existing_document: 'Replace existing document',
|
replace_existing_document: 'Replace existing document',
|
||||||
@@ -351,6 +352,7 @@ const es = {
|
|||||||
flip_horizontal: 'Voltear horizontal',
|
flip_horizontal: 'Voltear horizontal',
|
||||||
flip_vertical: 'Voltear vertical',
|
flip_vertical: 'Voltear vertical',
|
||||||
there_is_no_text_to_redact_on_this_page: 'Esta página contiene solo imágenes. La herramienta de censura solo puede usarse con páginas de texto',
|
there_is_no_text_to_redact_on_this_page: 'Esta página contiene solo imágenes. La herramienta de censura solo puede usarse con páginas de texto',
|
||||||
|
color: 'Color',
|
||||||
reset: 'Restablecer',
|
reset: 'Restablecer',
|
||||||
upload_to_document: 'Subir a "{document}"',
|
upload_to_document: 'Subir a "{document}"',
|
||||||
replace_existing_document: 'Reemplazar documento existente',
|
replace_existing_document: 'Reemplazar documento existente',
|
||||||
@@ -597,6 +599,7 @@ const it = {
|
|||||||
flip_horizontal: 'Rifletti orizzontale',
|
flip_horizontal: 'Rifletti orizzontale',
|
||||||
flip_vertical: 'Rifletti verticale',
|
flip_vertical: 'Rifletti verticale',
|
||||||
there_is_no_text_to_redact_on_this_page: 'Questa pagina contiene solo immagini. Lo strumento di oscuramento può essere usato solo con pagine di testo',
|
there_is_no_text_to_redact_on_this_page: 'Questa pagina contiene solo immagini. Lo strumento di oscuramento può essere usato solo con pagine di testo',
|
||||||
|
color: 'Colore',
|
||||||
reset: 'Reimposta',
|
reset: 'Reimposta',
|
||||||
upload_to_document: 'Carica in "{document}"',
|
upload_to_document: 'Carica in "{document}"',
|
||||||
replace_existing_document: 'Sostituisci documento esistente',
|
replace_existing_document: 'Sostituisci documento esistente',
|
||||||
@@ -837,6 +840,7 @@ const pt = {
|
|||||||
flip_horizontal: 'Inverter horizontal',
|
flip_horizontal: 'Inverter horizontal',
|
||||||
flip_vertical: 'Inverter vertical',
|
flip_vertical: 'Inverter vertical',
|
||||||
there_is_no_text_to_redact_on_this_page: 'Esta página contém apenas imagens. A ferramenta de censura só pode ser usada com páginas de texto',
|
there_is_no_text_to_redact_on_this_page: 'Esta página contém apenas imagens. A ferramenta de censura só pode ser usada com páginas de texto',
|
||||||
|
color: 'Cor',
|
||||||
reset: 'Redefinir',
|
reset: 'Redefinir',
|
||||||
upload_to_document: 'Enviar para "{document}"',
|
upload_to_document: 'Enviar para "{document}"',
|
||||||
replace_existing_document: 'Substituir documento existente',
|
replace_existing_document: 'Substituir documento existente',
|
||||||
@@ -1083,6 +1087,7 @@ const fr = {
|
|||||||
flip_horizontal: 'Miroir horizontal',
|
flip_horizontal: 'Miroir horizontal',
|
||||||
flip_vertical: 'Miroir vertical',
|
flip_vertical: 'Miroir vertical',
|
||||||
there_is_no_text_to_redact_on_this_page: 'Cette page ne contient que des images. L\'outil de caviardage ne peut être utilisé qu\'avec des pages de texte',
|
there_is_no_text_to_redact_on_this_page: 'Cette page ne contient que des images. L\'outil de caviardage ne peut être utilisé qu\'avec des pages de texte',
|
||||||
|
color: 'Couleur',
|
||||||
reset: 'Réinitialiser',
|
reset: 'Réinitialiser',
|
||||||
upload_to_document: 'Téléverser dans "{document}"',
|
upload_to_document: 'Téléverser dans "{document}"',
|
||||||
replace_existing_document: 'Remplacer le document existant',
|
replace_existing_document: 'Remplacer le document existant',
|
||||||
@@ -1326,6 +1331,7 @@ const de = {
|
|||||||
flip_horizontal: 'Horizontal spiegeln',
|
flip_horizontal: 'Horizontal spiegeln',
|
||||||
flip_vertical: 'Vertikal spiegeln',
|
flip_vertical: 'Vertikal spiegeln',
|
||||||
there_is_no_text_to_redact_on_this_page: 'Diese Seite enthält nur Bilder. Das Schwärzungswerkzeug kann nur mit Textseiten verwendet werden',
|
there_is_no_text_to_redact_on_this_page: 'Diese Seite enthält nur Bilder. Das Schwärzungswerkzeug kann nur mit Textseiten verwendet werden',
|
||||||
|
color: 'Farbe',
|
||||||
reset: 'Zurücksetzen',
|
reset: 'Zurücksetzen',
|
||||||
upload_to_document: 'In "{document}" hochladen',
|
upload_to_document: 'In "{document}" hochladen',
|
||||||
replace_existing_document: 'Vorhandenes Dokument ersetzen',
|
replace_existing_document: 'Vorhandenes Dokument ersetzen',
|
||||||
@@ -1569,6 +1575,7 @@ const nl = {
|
|||||||
flip_horizontal: 'Horizontaal spiegelen',
|
flip_horizontal: 'Horizontaal spiegelen',
|
||||||
flip_vertical: 'Verticaal spiegelen',
|
flip_vertical: 'Verticaal spiegelen',
|
||||||
there_is_no_text_to_redact_on_this_page: 'Deze pagina bevat alleen afbeeldingen. De redactietool kan alleen worden gebruikt met tekstpagina\'s',
|
there_is_no_text_to_redact_on_this_page: 'Deze pagina bevat alleen afbeeldingen. De redactietool kan alleen worden gebruikt met tekstpagina\'s',
|
||||||
|
color: 'Kleur',
|
||||||
reset: 'Opnieuw instellen',
|
reset: 'Opnieuw instellen',
|
||||||
upload_to_document: 'Uploaden naar "{document}"',
|
upload_to_document: 'Uploaden naar "{document}"',
|
||||||
replace_existing_document: 'Bestaand document vervangen',
|
replace_existing_document: 'Bestaand document vervangen',
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="inline-flex items-stretch replace-document-control">
|
<div
|
||||||
|
class="inline-flex items-stretch replace-document-control"
|
||||||
|
:class="{ 'opacity-100': isLoading }"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-neutral btn-xs transition-none replace-document-button"
|
class="btn btn-neutral btn-xs transition-none replace-document-button"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class ProcessSubmissionExpiredJob
|
|||||||
return if submission.archived_at?
|
return if submission.archived_at?
|
||||||
return if submission.template&.archived_at?
|
return if submission.template&.archived_at?
|
||||||
return if submission.submitters.where.not(declined_at: nil).exists?
|
return if submission.submitters.where.not(declined_at: nil).exists?
|
||||||
return unless submission.submitters.exists?(completed_at: nil)
|
return if submission.completed_at?
|
||||||
|
|
||||||
WebhookUrls.enqueue_events(submission, 'submission.expired')
|
WebhookUrls.enqueue_events(submission, 'submission.expired')
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,30 +5,38 @@ class ProcessSubmitterCompletionJob
|
|||||||
|
|
||||||
def perform(params = {})
|
def perform(params = {})
|
||||||
submitter = Submitter.find(params['submitter_id'])
|
submitter = Submitter.find(params['submitter_id'])
|
||||||
|
submission = submitter.submission
|
||||||
|
|
||||||
create_completed_submitter!(submitter)
|
create_completed_submitter!(submitter)
|
||||||
|
|
||||||
is_all_completed = !submitter.submission.submitters.exists?(completed_at: nil)
|
is_last =
|
||||||
|
if params.key?('is_last')
|
||||||
|
params['is_last']
|
||||||
|
else
|
||||||
|
!submission.submitters.exists?(completed_at: nil) &&
|
||||||
|
submitter.completed_at == submission.submitters.maximum(:completed_at)
|
||||||
|
end
|
||||||
|
|
||||||
Submissions::EnsureResultGenerated.call(submitter)
|
Submissions::EnsureResultGenerated.call(submitter)
|
||||||
|
|
||||||
if is_all_completed && submitter.completed_at == submitter.submission.submitters.maximum(:completed_at)
|
if is_last
|
||||||
if submitter.submission.account.account_configs.exists?(key: AccountConfig::COMBINE_PDF_RESULT_KEY, value: true)
|
if submission.account.account_configs.exists?(key: AccountConfig::COMBINE_PDF_RESULT_KEY, value: true)
|
||||||
Submissions::EnsureCombinedGenerated.call(submitter)
|
Submissions::EnsureCombinedGenerated.call(submitter)
|
||||||
end
|
end
|
||||||
|
|
||||||
Submissions::EnsureAuditGenerated.call(submitter.submission)
|
Submissions::EnsureAuditGenerated.call(submission)
|
||||||
|
|
||||||
enqueue_completed_emails(submitter)
|
enqueue_completed_emails(submitter)
|
||||||
end
|
end
|
||||||
|
|
||||||
create_completed_documents!(submitter)
|
create_completed_documents!(submitter)
|
||||||
|
|
||||||
if !is_all_completed && submitter.submission.submitters_order_preserved? && params['send_invitation_email'] != false
|
if !submission.completed_at && submission.submitters_order_preserved? && params['send_invitation_email'] != false &&
|
||||||
|
Submission.exists?(id: submission.id, completed_at: nil)
|
||||||
enqueue_next_submitter_request_notification(submitter)
|
enqueue_next_submitter_request_notification(submitter)
|
||||||
end
|
end
|
||||||
|
|
||||||
enqueue_completed_webhooks(submitter, is_all_completed:)
|
enqueue_completed_webhooks(submitter, is_last:)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_completed_submitter!(submitter)
|
def create_completed_submitter!(submitter)
|
||||||
@@ -77,7 +85,7 @@ class ProcessSubmitterCompletionJob
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def enqueue_completed_webhooks(submitter, is_all_completed: false)
|
def enqueue_completed_webhooks(submitter, is_last: false)
|
||||||
event_uuids = {}
|
event_uuids = {}
|
||||||
|
|
||||||
WebhookUrls.for_account_id(submitter.account_id, %w[form.completed submission.completed]).each do |webhook|
|
WebhookUrls.for_account_id(submitter.account_id, %w[form.completed submission.completed]).each do |webhook|
|
||||||
@@ -89,7 +97,7 @@ class ProcessSubmitterCompletionJob
|
|||||||
'webhook_url_id' => webhook.id)
|
'webhook_url_id' => webhook.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
next unless webhook.events.include?('submission.completed') && is_all_completed
|
next unless webhook.events.include?('submission.completed') && is_last
|
||||||
|
|
||||||
event_uuids['submission.completed'] ||= SecureRandom.uuid
|
event_uuids['submission.completed'] ||= SecureRandom.uuid
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ class SendSubmitterInvitationEmailJob
|
|||||||
submitter = Submitter.find(params['submitter_id'])
|
submitter = Submitter.find(params['submitter_id'])
|
||||||
|
|
||||||
return if submitter.completed_at?
|
return if submitter.completed_at?
|
||||||
|
return if submitter.declined_at?
|
||||||
return if submitter.submission.archived_at?
|
return if submitter.submission.archived_at?
|
||||||
|
return if submitter.submission.expired?
|
||||||
return if submitter.template&.archived_at?
|
return if submitter.template&.archived_at?
|
||||||
return if submitter.submission.source == 'invite' && !Accounts.can_send_emails?(submitter.account, on_events: true)
|
return if submitter.submission.source == 'invite' && !Accounts.can_send_emails?(submitter.account, on_events: true)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
|
|
||||||
template_submitters_index = @email_message.blank? ? build_submitter_preferences_index(@submitter) : {}
|
template_submitters_index = @email_message.blank? ? build_submitter_preferences_index(@submitter) : {}
|
||||||
|
|
||||||
@body = @email_message&.body.presence ||
|
@body = @email_message&.normalized_body.presence ||
|
||||||
template_submitters_index.dig(@submitter.uuid, 'request_email_body').presence ||
|
template_submitters_index.dig(@submitter.uuid, 'request_email_body').presence ||
|
||||||
@submitter.template&.preferences&.dig('request_email_body').presence
|
@submitter.template&.preferences&.dig('request_email_body').presence
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
to: user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name,
|
to: user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name,
|
||||||
reply_to: @submitter.friendly_name,
|
reply_to: @submitter.friendly_name,
|
||||||
subject: I18n.t(:name_declined_by_submitter,
|
subject: I18n.t(:name_declined_by_submitter,
|
||||||
name: (@submission.name || @submission.template.name).truncate(20),
|
name: (@submission.name || @submission.template&.name).to_s.truncate(20),
|
||||||
submitter: @submitter.name || @submitter.email || @submitter.phone))
|
submitter: @submitter.name || @submitter.email || @submitter.phone))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -144,6 +144,7 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def otp_verification_email(submitter, locale: nil)
|
def otp_verification_email(submitter, locale: nil)
|
||||||
|
@current_account = submitter.account
|
||||||
@submitter = submitter
|
@submitter = submitter
|
||||||
@otp_code = EmailVerificationCodes.generate([submitter.email.downcase.strip, submitter.slug].join(':'))
|
@otp_code = EmailVerificationCodes.generate([submitter.email.downcase.strip, submitter.slug].join(':'))
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class Account < ApplicationRecord
|
|||||||
has_many :encrypted_configs, dependent: :destroy
|
has_many :encrypted_configs, dependent: :destroy
|
||||||
has_many :account_configs, dependent: :destroy
|
has_many :account_configs, dependent: :destroy
|
||||||
has_many :email_messages, dependent: :destroy
|
has_many :email_messages, dependent: :destroy
|
||||||
|
has_many :email_message_assets, dependent: :destroy
|
||||||
has_many :templates, dependent: :destroy
|
has_many :templates, dependent: :destroy
|
||||||
has_many :template_folders, dependent: :destroy
|
has_many :template_folders, dependent: :destroy
|
||||||
has_one :default_template_folder, -> { where(name: TemplateFolder::DEFAULT_NAME) },
|
has_one :default_template_folder, -> { where(name: TemplateFolder::DEFAULT_NAME) },
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ class EmailMessage < ApplicationRecord
|
|||||||
|
|
||||||
before_validation :set_sha1, on: :create
|
before_validation :set_sha1, on: :create
|
||||||
|
|
||||||
|
def normalized_body
|
||||||
|
@normalized_body ||=
|
||||||
|
if body&.include?(EmailMessages::ASSET_PREFIX)
|
||||||
|
EmailMessages.rebuild_body_with_assets(account_id, body)
|
||||||
|
else
|
||||||
|
body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_sha1
|
def set_sha1
|
||||||
self.sha1 = Digest::SHA1.hexdigest({ subject:, body: }.to_json)
|
self.sha1 = Digest::SHA1.hexdigest({ subject:, body: }.to_json)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: email_message_assets
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# data :text not null
|
||||||
|
# sha1 :string not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_email_message_assets_on_account_id_and_sha1 (account_id,sha1) UNIQUE
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (account_id => accounts.id)
|
||||||
|
#
|
||||||
|
class EmailMessageAsset < ApplicationRecord
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
before_validation :set_sha1, on: :create
|
||||||
|
|
||||||
|
def set_sha1
|
||||||
|
self.sha1 = Digest::SHA1.hexdigest(data.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
+12
-13
@@ -6,6 +6,7 @@
|
|||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# archived_at :datetime
|
# archived_at :datetime
|
||||||
|
# completed_at :datetime
|
||||||
# expire_at :datetime
|
# expire_at :datetime
|
||||||
# name :text
|
# name :text
|
||||||
# preferences :text not null
|
# preferences :text not null
|
||||||
@@ -25,7 +26,9 @@
|
|||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_submissions_on_account_id_and_completed_at (account_id,completed_at) WHERE ((completed_at IS NOT NULL) AND (archived_at IS NULL))
|
||||||
# index_submissions_on_account_id_and_id (account_id,id)
|
# index_submissions_on_account_id_and_id (account_id,id)
|
||||||
|
# index_submissions_on_account_id_and_id_pending (account_id,id) WHERE ((completed_at IS NULL) AND (archived_at IS NULL))
|
||||||
# index_submissions_on_account_id_and_template_id_and_id (account_id,template_id,id) WHERE (archived_at IS NULL)
|
# index_submissions_on_account_id_and_template_id_and_id (account_id,template_id,id) WHERE (archived_at IS NULL)
|
||||||
# index_submissions_on_account_id_and_template_id_and_id_archived (account_id,template_id,id) WHERE (archived_at IS NOT NULL)
|
# index_submissions_on_account_id_and_template_id_and_id_archived (account_id,template_id,id) WHERE (archived_at IS NOT NULL)
|
||||||
# index_submissions_on_created_by_user_id (created_by_user_id)
|
# index_submissions_on_created_by_user_id (created_by_user_id)
|
||||||
@@ -89,22 +92,18 @@ class Submission < ApplicationRecord
|
|||||||
scope :active, -> { where(archived_at: nil) }
|
scope :active, -> { where(archived_at: nil) }
|
||||||
scope :archived, -> { where.not(archived_at: nil) }
|
scope :archived, -> { where.not(archived_at: nil) }
|
||||||
scope :pending, lambda {
|
scope :pending, lambda {
|
||||||
where(expire_at: nil).or(where(expire_at: Time.current..))
|
where(expire_at: nil).or(where(expire_at: Time.current..)).where(completed_at: nil)
|
||||||
.where(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
|
||||||
.and(Submitter.arel_table[:completed_at].eq(nil))).select(1).arel.exists)
|
|
||||||
}
|
|
||||||
scope :completed, lambda {
|
|
||||||
where.not(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
|
||||||
.and(Submitter.arel_table[:completed_at].eq(nil))).select(1).arel.exists)
|
|
||||||
}
|
}
|
||||||
|
scope :completed, -> { where.not(completed_at: nil) }
|
||||||
scope :declined, lambda {
|
scope :declined, lambda {
|
||||||
where(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
where(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id]))
|
||||||
.and(Submitter.arel_table[:declined_at].not_eq(nil))).select(1).arel.exists)
|
.where.not(declined_at: nil).limit(1).arel.exists)
|
||||||
}
|
}
|
||||||
scope :expired, lambda {
|
scope :expired, -> { where(expire_at: ..Time.current).where(completed_at: nil) }
|
||||||
where(expire_at: ..Time.current)
|
|
||||||
.where(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
scope :select_for_list, lambda {
|
||||||
.and(Submitter.arel_table[:completed_at].eq(nil))).select(1).arel.exists)
|
select(:id, :name, :created_by_user_id, :account_id, :completed_at,
|
||||||
|
:created_at, :archived_at, :expire_at, :template_id, :template_submitters)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum :source, {
|
enum :source, {
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_submitters_on_account_id_and_completed_at (account_id,completed_at) WHERE (completed_at IS NOT NULL)
|
||||||
# index_submitters_on_account_id_and_id (account_id,id)
|
# index_submitters_on_account_id_and_id (account_id,id)
|
||||||
# index_submitters_on_completed_at_and_account_id (completed_at,account_id)
|
|
||||||
# index_submitters_on_email (email)
|
# index_submitters_on_email (email)
|
||||||
# index_submitters_on_external_id (external_id)
|
# index_submitters_on_external_id (external_id)
|
||||||
# index_submitters_on_slug (slug) UNIQUE
|
# index_submitters_on_slug (slug) UNIQUE
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ class Template < ApplicationRecord
|
|||||||
scope :active, -> { where(archived_at: nil) }
|
scope :active, -> { where(archived_at: nil) }
|
||||||
scope :archived, -> { where.not(archived_at: nil) }
|
scope :archived, -> { where.not(archived_at: nil) }
|
||||||
|
|
||||||
|
scope :select_for_list, -> { select(:id, :name, :author_id, :account_id, :created_at, :archived_at, :folder_id) }
|
||||||
|
|
||||||
def application_key
|
def application_key
|
||||||
external_id
|
external_id
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<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="M10 19h-5a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2h4l3 3h7a2 2 0 0 1 2 2v2.5"></path>
|
||||||
|
<path d="M17.8 20.817l-2.172 1.138a.392 .392 0 0 1 -.568 -.41l.415 -2.411l-1.757 -1.707a.389 .389 0 0 1 .217 -.665l2.428 -.352l1.086 -2.193a.392 .392 0 0 1 .702 0l1.086 2.193l2.428 .352a.39 .39 0 0 1 .217 .665l-1.757 1.707l.414 2.41a.39 .39 0 0 1 -.567 .411l-2.172 -1.138"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 662 B |
@@ -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 19h-7a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2h4l3 3h7a2 2 0 0 1 2 2v3.5"></path>
|
||||||
|
<path d="M19 22v-6"></path>
|
||||||
|
<path d="M22 19l-3 -3l-3 3"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 447 B |
@@ -36,7 +36,7 @@
|
|||||||
<% if signature %>
|
<% if signature %>
|
||||||
<div class="flex justify-center mb-4 relative">
|
<div class="flex justify-center mb-4 relative">
|
||||||
<%= button_to button_title(title: t('remove'), disabled_with: t('removing')), user_signature_path, method: :delete, class: 'right-0 top-0 absolute link' %>
|
<%= button_to button_title(title: t('remove'), disabled_with: t('removing')), user_signature_path, method: :delete, class: 'right-0 top-0 absolute link' %>
|
||||||
<img src="<%= signature.url %>" style="height: 130px; width: 100%; object-fit: contain;">
|
<img src="<%= signature.url(time: ActiveStorage::Attachment.service_url_time) %>" style="height: 130px; width: 100%; object-fit: contain;">
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<a href="<%= edit_user_signature_path %>" data-turbo-frame="modal" class="base-button w-full">
|
<a href="<%= edit_user_signature_path %>" data-turbo-frame="modal" class="base-button w-full">
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<% if initials %>
|
<% if initials %>
|
||||||
<div class="flex justify-center mb-4 relative">
|
<div class="flex justify-center mb-4 relative">
|
||||||
<%= button_to button_title(title: t('remove'), disabled_with: t('removing')), user_initials_path, method: :delete, class: 'right-0 top-0 absolute link' %>
|
<%= button_to button_title(title: t('remove'), disabled_with: t('removing')), user_initials_path, method: :delete, class: 'right-0 top-0 absolute link' %>
|
||||||
<img src="<%= initials.url %>" style="height: 130px; width: 100%; object-fit: contain;">
|
<img src="<%= initials.url(time: ActiveStorage::Attachment.service_url_time) %>" style="height: 130px; width: 100%; object-fit: contain;">
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<a href="<%= edit_user_initials_path %>" data-turbo-frame="modal" class="base-button w-full">
|
<a href="<%= edit_user_initials_path %>" data-turbo-frame="modal" class="base-button w-full">
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= f.label :current_password, t('current_password'), class: 'label' %>
|
<%= f.label :current_password, t('current_password'), class: 'label' %>
|
||||||
<%= f.password_field :current_password, autocomplete: 'current-password', class: 'base-input' %>
|
<%= f.password_field :current_password, autocomplete: 'current-password', class: 'base-input' %>
|
||||||
<% if Accounts.can_send_emails?(current_account) %>
|
<% if Docuseal.multitenant? || Accounts.can_send_emails?(current_account) %>
|
||||||
<span class="label-text-alt mt-1">
|
<span class="label-text-alt mt-1">
|
||||||
<%= t('dont_remember_your_current_password_click_here_to_reset_it_html') %>
|
<%= t('dont_remember_your_current_password_click_here_to_reset_it_html') %>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
<p>
|
<p>
|
||||||
<% if @current_account&.testing? %>
|
<% if @current_account&.testing? %>
|
||||||
<%= t('sent_using_product_name_in_testing_mode_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
<%= t('sent_using_product_name_in_testing_mode_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
||||||
<% else %>
|
<% elsif Docuseal.multitenant? %>
|
||||||
<%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
<%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
||||||
|
<% else %>
|
||||||
|
<%= t('sent_using_product_name_open_source_software_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/open", product_name: Docuseal.product_name) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
@@ -71,10 +71,11 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<li>
|
<li>
|
||||||
<%= button_to destroy_user_session_path, method: :delete, data: { turbo: false }, class: 'flex items-center' do %>
|
<button form="destroy_user_session_form">
|
||||||
<%= svg_icon('logout', class: 'w-5 h-5 flex-shrink-0 stroke-2 mr-2 inline') %>
|
<%= svg_icon('logout', class: 'w-5 h-5 flex-shrink-0 stroke-2 inline') %>
|
||||||
<span class="mr-1 whitespace-nowrap"><%= t('sign_out') %></span>
|
<span class="whitespace-nowrap"><%= t('sign_out') %></span>
|
||||||
<% end %>
|
</button>
|
||||||
|
<%= button_to '', destroy_user_session_path, method: :delete, data: { turbo: false }, form: { id: 'destroy_user_session_form' }, form_class: 'hidden' %>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
<input name="<%= key %>" value="<%= params[key] %>" class="hidden">
|
<input name="<%= key %>" value="<%= params[key] %>" class="hidden">
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% if params[:archived].present? %>
|
||||||
|
<input name="archived" value="<%= params[:archived] %>" class="hidden">
|
||||||
|
<% end %>
|
||||||
<% if params[:q].present? %>
|
<% if params[:q].present? %>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<a href="<%= url_for(params: request.query_parameters.except('q')) %>" title="<%= t('clear') %>" class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-auto text-neutral text-2xl font-extralight">
|
<a href="<%= url_for(params: request.query_parameters.except('q')) %>" title="<%= t('clear') %>" class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-auto text-neutral text-2xl font-extralight">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p dir="auto" class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p>
|
<p dir="auto" class="text-lg font-bold mb-1"><%= @submitter.submission.template&.name %></p>
|
||||||
<p dir="auto" class="text-sm">
|
<p dir="auto" class="text-sm">
|
||||||
<%= t(@submitter.with_signature_fields? ? 'signed_on_time' : 'completed_on_time', time: l(@submitter.completed_at.to_date, format: :long)) %>
|
<%= t(@submitter.with_signature_fields? ? 'signed_on_time' : 'completed_on_time', time: l(@submitter.completed_at.to_date, format: :long)) %>
|
||||||
</p>
|
</p>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<%= button_to button_title(title: t('send_copy_to_email'), disabled_with: t('sending'), icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { template_slug: @template.slug, email: params[:email] }, class: 'base-button w-full' %>
|
<%= button_to button_title(title: t('send_copy_to_email'), disabled_with: t('sending'), icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { template_slug: @template.slug, email: params[:email] }, class: 'base-button w-full' %>
|
||||||
</toggle-submit>
|
</toggle-submit>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if Templates.filter_undefined_submitters(@template.submitters).size == 1 && %w[api embed].exclude?(@submitter.submission.source) && @submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false && @template.shared_link? %>
|
<% if Templates.filter_undefined_submitters(@template.submitters).size == 1 && %w[api embed mcp].exclude?(@submitter.submission.source) && @submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false && @template.shared_link? %>
|
||||||
<toggle-submit class="block">
|
<toggle-submit class="block">
|
||||||
<%= button_to button_title(title: t('resubmit'), disabled_with: t('resubmit'), icon: svg_icon('reload', class: 'w-6 h-6')), start_form_path(@template.slug), params: { submitter: params.permit(:name, :email, :phone).compact_blank, resubmit: true }, method: :put, class: 'white-button w-full' %>
|
<%= button_to button_title(title: t('resubmit'), disabled_with: t('resubmit'), icon: svg_icon('reload', class: 'w-6 h-6')), start_form_path(@template.slug), params: { submitter: params.permit(:name, :email, :phone).compact_blank, resubmit: true }, method: :put, class: 'white-button w-full' %>
|
||||||
</toggle-submit>
|
</toggle-submit>
|
||||||
|
|||||||
@@ -91,8 +91,8 @@
|
|||||||
</dynamic-list>
|
</dynamic-list>
|
||||||
<%= local_assigns[:variables_form] %>
|
<%= local_assigns[:variables_form] %>
|
||||||
<div>
|
<div>
|
||||||
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %>
|
<%= render('submitters_order', f:, template:) if can_send_emails %>
|
||||||
<%= render 'send_email', f:, template: %>
|
<%= render 'send_email', f:, template:, can_send_emails: %>
|
||||||
<% if has_phone_field %>
|
<% if has_phone_field %>
|
||||||
<%= render 'send_sms', f: %>
|
<%= render 'send_sms', f: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -69,8 +69,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<%= local_assigns[:variables_form] %>
|
<%= local_assigns[:variables_form] %>
|
||||||
<div>
|
<div>
|
||||||
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %>
|
<%= render('submitters_order', f:, template:) if can_send_emails %>
|
||||||
<%= render 'send_email', f:, template: %>
|
<%= render 'send_email', f:, template:, can_send_emails: %>
|
||||||
<%= render 'extra_fields', f: %>
|
<%= render 'extra_fields', f: %>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
</dynamic-list>
|
</dynamic-list>
|
||||||
<%= local_assigns[:variables_form] %>
|
<%= local_assigns[:variables_form] %>
|
||||||
<div>
|
<div>
|
||||||
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %>
|
<%= render 'submitters_order', f:, template: %>
|
||||||
<%= render 'send_sms', f:, checked: true %>
|
<%= render 'send_sms', f:, checked: true %>
|
||||||
<%= render 'extra_phone_fields', f: %>
|
<%= render 'extra_phone_fields', f: %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,15 +2,14 @@
|
|||||||
<% template_submitters = local_assigns[:submitter]&.submission&.template_submitters || template.submitters %>
|
<% template_submitters = local_assigns[:submitter]&.submission&.template_submitters || template.submitters %>
|
||||||
<% message_field_id = "message_field_#{SecureRandom.hex(3)}" %>
|
<% message_field_id = "message_field_#{SecureRandom.hex(3)}" %>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<% can_send_emails = Accounts.can_send_emails?(current_account) %>
|
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<%= f.label :send_email, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
|
<%= 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: !can_send_emails || local_assigns[:disable_email], checked: can_send_emails && !local_assigns.key?(:resend_email) && !local_assigns[:disable_email] && template&.preferences&.dig('request_email_enabled') != false %>
|
<%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails || local_assigns[:disable_email], checked: can_send_emails && !local_assigns.key?(:resend_email) && !local_assigns[:disable_email] && template&.preferences&.dig('request_email_enabled') != false %>
|
||||||
<span class="label"><%= local_assigns[:resend_email] ? t('re_send_email') : t('send_email') %></span>
|
<span class="label"><%= local_assigns[:resend_email] ? t('re_send_email') : t('send_email') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div>
|
<div>
|
||||||
<% if can_send_emails %>
|
|
||||||
<%= render 'submissions/email_stats' %>
|
<%= render 'submissions/email_stats' %>
|
||||||
|
<% if can_send_emails %>
|
||||||
<%= content_for(:edit_button) || capture do %>
|
<%= content_for(:edit_button) || capture do %>
|
||||||
<toggle-visible data-element-ids="<%= [message_field_id].to_json %>" class="flex">
|
<toggle-visible data-element-ids="<%= [message_field_id].to_json %>" class="flex">
|
||||||
<label>
|
<label>
|
||||||
@@ -57,7 +56,7 @@
|
|||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= f.label :message, t('body'), class: 'label' %>
|
<%= f.label :message, t('body'), class: 'label' %>
|
||||||
<% body_variables = AccountConfig::EMAIL_VARIABLES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY] %>
|
<% body_variables = AccountConfig::EMAIL_VARIABLES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY] %>
|
||||||
<%= render 'personalization_settings/markdown_editor', name: f.field_name(:body), value: local_assigns[:submitter_email_message]&.body.presence || submitter_preferences_index.dig(local_assigns[:submitter]&.uuid, 'request_email_body').presence || template&.preferences&.dig('request_email_body').presence || config.value['body'], variables: body_variables %>
|
<%= render 'personalization_settings/markdown_editor', name: f.field_name(:body), value: local_assigns[:submitter_email_message]&.normalized_body.presence || submitter_preferences_index.dig(local_assigns[:submitter]&.uuid, 'request_email_body').presence || template&.preferences&.dig('request_email_body').presence || config.value['body'], variables: body_variables %>
|
||||||
<% unless local_assigns.fetch(:disable_save_as_default_template_option, false) %>
|
<% unless local_assigns.fetch(:disable_save_as_default_template_option, false) %>
|
||||||
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
|
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
|
||||||
<%= check_box_tag :save_message, id: uuid, class: 'base-checkbox', checked: false %>
|
<%= check_box_tag :save_message, id: uuid, class: 'base-checkbox', checked: false %>
|
||||||
@@ -92,7 +91,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= ff.label :message, t('body'), class: 'label' %>
|
<%= ff.label :message, t('body'), class: 'label' %>
|
||||||
<%= render 'personalization_settings/markdown_editor', name: ff.field_name(:body), value: local_assigns[:submitter_email_message]&.body.presence || submitter_preferences_index.dig(submitter['uuid'], 'request_email_body').presence || template&.preferences&.dig('request_email_body').presence || config.value['body'], variables: body_variables %>
|
<%= render 'personalization_settings/markdown_editor', name: ff.field_name(:body), value: local_assigns[:submitter_email_message]&.normalized_body.presence || submitter_preferences_index.dig(submitter['uuid'], 'request_email_body').presence || template&.preferences&.dig('request_email_body').presence || config.value['body'], variables: body_variables %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<% if template.preferences['submitters_order'] == 'preserved' || template.submitters.any? { |s| s['order'] } || (template.submitters.size > 1 && template.fields.any? { |f| f['type'] == 'verification' }) %>
|
<% if template.preferences['submitters_order'] == 'preserved' || template.submitters.any? { |s| s['order'] } || (template.submitters.size > 1 && template.fields.any? { |f| f['type'] == 'verification' }) %>
|
||||||
<%= f.hidden_field :preserve_order, value: '1' %>
|
<%= f.hidden_field :preserve_order, value: '1' %>
|
||||||
<% elsif template.submitters.size > 1 %>
|
<% elsif template.submitters.size > 1 %>
|
||||||
|
<% account_submissions = template.submissions.where(account_id: current_account.id) %>
|
||||||
|
<% last_submission = account_submissions.active.last || account_submissions.archived.last %>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= f.label :preserve_order, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
|
<%= f.label :preserve_order, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
|
||||||
<%= f.check_box :preserve_order, id: uuid, class: 'base-checkbox', checked: template.submissions.last&.submitters_order.in?(['preserved', nil]) %>
|
<%= f.check_box :preserve_order, id: uuid, class: 'base-checkbox', checked: last_submission&.submitters_order.in?(['preserved', nil]) %>
|
||||||
<span class="label"><%= t('preserve_order') %></span>
|
<span class="label"><%= t('preserve_order') %></span>
|
||||||
<span class="tooltip" data-tip="<%= t('when_checked_notifications_will_be_sent_to_the_second_party_once_the_form_is_completed_by_the_previous_party_uncheck_this_option_to_send_notifications_to_all_parties_simultaneously_right_away') %>">
|
<span class="tooltip" data-tip="<%= t('when_checked_notifications_will_be_sent_to_the_second_party_once_the_form_is_completed_by_the_previous_party_uncheck_this_option_to_send_notifications_to_all_parties_simultaneously_right_away') %>">
|
||||||
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
|
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<% is_narrow = area['h'].to_f.positive? && ((area['w'].to_f * local_assigns[:page_width]) / (area['h'].to_f * local_assigns[:page_height])) > 4.5 %>
|
<% is_narrow = area['h'].to_f.positive? && ((area['w'].to_f * local_assigns[:page_width]) / (area['h'].to_f * local_assigns[:page_height])) > 4.5 %>
|
||||||
<div class="flex justify-between w-full h-full gap-1 <%= is_narrow && (local_assigns[:with_signature_id] || field.dig('preferences', 'reason_field_uuid').present?) ? 'flex-row' : 'flex-col' %>">
|
<div class="flex justify-between w-full h-full gap-1 <%= is_narrow && (local_assigns[:with_signature_id] || field.dig('preferences', 'reason_field_uuid').present?) ? 'flex-row' : 'flex-col' %>">
|
||||||
<div class="flex overflow-hidden <%= is_narrow && (local_assigns[:with_signature_id] || field.dig('preferences', 'reason_field_uuid').present?) ? 'w-1/2' : 'flex-grow' %>" style="min-height: 50%">
|
<div class="flex overflow-hidden <%= is_narrow && (local_assigns[:with_signature_id] || field.dig('preferences', 'reason_field_uuid').present?) ? 'w-1/2' : 'flex-grow' %>" style="min-height: 50%">
|
||||||
<img class="object-contain mx-auto" src="<%= attachments_index[value].url %>" alt="<%= field['name'].presence || field['title'].presence || field['type'] %>">
|
<img class="object-contain mx-auto" src="<%= attachments_index[value].url(time: ActiveStorage::Attachment.service_url_time) %>" alt="<%= field['name'].presence || field['title'].presence || field['type'] %>">
|
||||||
</div>
|
</div>
|
||||||
<% if (local_assigns[:with_signature_id] || field.dig('preferences', 'reason_field_uuid').present?) && attachment = attachments_index[value] %>
|
<% if (local_assigns[:with_signature_id] || field.dig('preferences', 'reason_field_uuid').present?) && attachment = attachments_index[value] %>
|
||||||
<div class="text-[1vw] lg:text-[0.55rem] lg:leading-[0.65rem] <%= is_narrow ? 'w-1/2' : 'w-full' %>">
|
<div class="text-[1vw] lg:text-[0.55rem] lg:leading-[0.65rem] <%= is_narrow ? 'w-1/2' : 'w-full' %>">
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% elsif field['type'].in?(['image', 'initials', 'stamp', 'kba']) && attachments_index[value].image? %>
|
<% elsif field['type'].in?(['image', 'initials', 'stamp', 'kba']) && attachments_index[value].image? %>
|
||||||
<img class="object-contain mx-auto" src="<%= attachments_index[value].url %>" loading="lazy" alt="<%= field['name'].presence || field['title'].presence || field['type'] %>">
|
<img class="object-contain mx-auto" src="<%= attachments_index[value].url(time: ActiveStorage::Attachment.service_url_time) %>" loading="lazy" alt="<%= field['name'].presence || field['title'].presence || field['type'] %>">
|
||||||
<% elsif field['type'].in?(['file', 'payment', 'image']) %>
|
<% elsif field['type'].in?(['file', 'payment', 'image']) %>
|
||||||
<autosize-field></autosize-field>
|
<autosize-field></autosize-field>
|
||||||
<div class="px-0.5 flex flex-col justify-center">
|
<div class="px-0.5 flex flex-col justify-center">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<% prefillable_fields = @template.fields.select { |f| f['prefillable'] } %>
|
<% prefillable_fields = @template.fields.select { |f| f['prefillable'] } %>
|
||||||
<% default_tab = cookies.permanent[:add_recipients_tab].presence || 'email' %>
|
<% default_tab = cookies.permanent[:add_recipients_tab].presence || 'email' %>
|
||||||
<% recipient_form_fields = Accounts.load_recipient_form_fields(current_account) if prefillable_fields.blank? %>
|
<% recipient_form_fields = Accounts.load_recipient_form_fields(current_account) if prefillable_fields.blank? %>
|
||||||
|
<% can_send_emails = Accounts.can_send_emails?(current_account) %>
|
||||||
<% only_detailed = require_phone_2fa || require_email_2fa || prefillable_fields.present? || recipient_form_fields.present? %>
|
<% only_detailed = require_phone_2fa || require_email_2fa || prefillable_fields.present? || recipient_form_fields.present? %>
|
||||||
<% with_list = @template.variables_schema.blank? %>
|
<% with_list = @template.variables_schema.blank? %>
|
||||||
<% variables_form = render 'variables_form', schema: @template.variables_schema if @template.variables_schema.present? && @template.variables_schema.any? { |_, v| !v['disabled'] } %>
|
<% variables_form = render 'variables_form', schema: @template.variables_schema if @template.variables_schema.present? && @template.variables_schema.any? { |_, v| !v['disabled'] } %>
|
||||||
@@ -25,18 +26,18 @@
|
|||||||
<div class="px-5 mb-5 mt-4">
|
<div class="px-5 mb-5 mt-4">
|
||||||
<% unless only_detailed %>
|
<% unless only_detailed %>
|
||||||
<div id="email" class="<%= 'hidden' if default_tab != 'email' %>">
|
<div id="email" class="<%= 'hidden' if default_tab != 'email' %>">
|
||||||
<%= render 'email_form', template: @template, variables_form: %>
|
<%= render 'email_form', template: @template, variables_form:, can_send_emails: %>
|
||||||
</div>
|
</div>
|
||||||
<div id="phone" class="<%= 'hidden' if default_tab != 'phone' %>">
|
<div id="phone" class="<%= 'hidden' if default_tab != 'phone' %>">
|
||||||
<%= render 'phone_form', template: @template, variables_form: %>
|
<%= render 'phone_form', template: @template, variables_form: %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div id="detailed" class="<%= 'hidden' if !only_detailed && default_tab != 'detailed' %>">
|
<div id="detailed" class="<%= 'hidden' if !only_detailed && default_tab != 'detailed' %>">
|
||||||
<%= render 'detailed_form', template: @template, require_phone_2fa:, require_email_2fa:, prefillable_fields:, recipient_form_fields:, variables_form: %>
|
<%= render 'detailed_form', template: @template, require_phone_2fa:, require_email_2fa:, prefillable_fields:, recipient_form_fields:, variables_form:, can_send_emails: %>
|
||||||
</div>
|
</div>
|
||||||
<% if with_list %>
|
<% if with_list %>
|
||||||
<div id="list" class="hidden">
|
<div id="list" class="hidden">
|
||||||
<%= render 'list_form', template: @template %>
|
<%= render 'list_form', template: @template, can_send_emails: %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'submissions/error' %>
|
<%= render 'submissions/error' %>
|
||||||
|
|||||||
@@ -12,11 +12,10 @@
|
|||||||
<div class="flex justify-between py-1.5 items-center pr-4 sticky top-0 md:relative z-10 bg-base-100">
|
<div class="flex justify-between py-1.5 items-center pr-4 sticky top-0 md:relative z-10 bg-base-100">
|
||||||
<a href="<%= signed_in? && @submission.account_id == current_account&.id && @submission.template ? template_path(@submission.template) : '/' %>" class="flex items-center space-x-3 py-1">
|
<a href="<%= signed_in? && @submission.account_id == current_account&.id && @submission.template ? template_path(@submission.template) : '/' %>" class="flex items-center space-x-3 py-1">
|
||||||
<span><%= render 'submissions/logo' %></span>
|
<span><%= render 'submissions/logo' %></span>
|
||||||
<h1 class="text-xl md:text-3xl font-semibold focus:text-clip" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;"><% (@submission.name || @submission.template.name).split(/(_)/).each do |item| %><%= item %><wbr><% end %></h1>
|
<h1 class="text-xl md:text-3xl font-semibold focus:text-clip" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;"><% (@submission.name || @submission.template&.name).to_s.split(/(_)/).each do |item| %><%= item %><wbr><% end %></h1>
|
||||||
</a>
|
</a>
|
||||||
<div class="space-x-3 flex items-center">
|
<div class="space-x-3 flex items-center">
|
||||||
<% is_all_completed = @submission.submitters.to_a.all?(&:completed_at?) %>
|
<% if signed_in? && can?(:destroy, @submission) && @submission.archived_at? && !@submission.completed_at? %>
|
||||||
<% if signed_in? && can?(:create, @submission) && @submission.archived_at? && !is_all_completed %>
|
|
||||||
<%= button_to button_title(title: t('unarchive'), disabled_with: t('unarchive')[0..-2], icon: svg_icon('rotate', class: 'w-6 h-6')), submission_unarchive_index_path(@submission), class: 'btn btn-primary btn-ghost text-base hidden md:flex' %>
|
<%= button_to button_title(title: t('unarchive'), disabled_with: t('unarchive')[0..-2], icon: svg_icon('rotate', class: 'w-6 h-6')), submission_unarchive_index_path(@submission), class: 'btn btn-primary btn-ghost text-base hidden md:flex' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if @submission.audit_trail.present? %>
|
<% if @submission.audit_trail.present? %>
|
||||||
@@ -30,16 +29,19 @@
|
|||||||
<span class="hidden md:block"><%= t('event_log') %></span>
|
<span class="hidden md:block"><%= t('event_log') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if signed_in? && !is_all_completed && can?(:manage, :resend_all) && @submission.submitters.to_a.size > 3 && !@submission.archived_at? && !@submission.template&.archived_at? && !@submission.expired? && can?(:update, @submission) %>
|
<% if signed_in? && !@submission.completed_at? && can?(:manage, :resend_all) && @submission.submitters.to_a.size > 3 && !@submission.archived_at? && !@submission.template&.archived_at? && !@submission.expired? && can?(:update, @submission) %>
|
||||||
<% pending_submitters_count = @submission.submitters.to_a.count { |s| !s.completed_at? && s.email.present? && !s.declined_at? } %>
|
<% pending_submitters_count = @submission.submitters.to_a.count { |s| !s.completed_at? && s.email.present? && !s.declined_at? } %>
|
||||||
<% if pending_submitters_count.positive? %>
|
<% if pending_submitters_count.positive? %>
|
||||||
<%= button_to button_title(title: t('re_send_emails'), title_class: 'hidden md:inline', disabled_with: t('sending'), icon: svg_icon('mail_forward', class: 'w-6 h-6')), submission_resend_email_index_path(@submission), class: 'white-button', data: { turbo_confirm: t('are_you_sure_you_want_to_re_send_email_to_n_recipients', count: pending_submitters_count) } %>
|
<%= button_to button_title(title: t('re_send_emails'), title_class: 'hidden md:inline', disabled_with: t('sending'), icon: svg_icon('mail_forward', class: 'w-6 h-6')), submission_resend_email_index_path(@submission), class: 'white-button', data: { turbo_confirm: t('are_you_sure_you_want_to_re_send_email_to_n_recipients', count: pending_submitters_count) } %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if @submission.submitters.to_a.any?(&:completed_at?) %>
|
<% if @submission.submitters.to_a.any?(&:completed_at?) %>
|
||||||
<% if is_all_completed || !is_combined_enabled %>
|
<% show_combined_download = @submission.completed_at? && !is_combined_enabled %>
|
||||||
|
<% show_unarchive = signed_in? && can?(:destroy, @submission) && @submission.archived_at? && @submission.completed_at? %>
|
||||||
|
<% show_download_dropdown = show_combined_download || show_unarchive %>
|
||||||
|
<% if @submission.completed_at? || !is_combined_enabled %>
|
||||||
<div class="join relative">
|
<div class="join relative">
|
||||||
<download-button role="button" tabindex="0" aria-label="<%= t('download') %>" data-src="<%= local_assigns[:is_preview] ? (@sig_submitter ? submit_form_documents_path(@sig_submitter.slug, { sig: params[:sig], combined: is_combined_enabled }.compact_blank) : submissions_preview_download_index_path(@submission.slug, combined: is_combined_enabled.presence)) : submission_download_index_path(@submission, combined: is_combined_enabled.presence) %>" class="base-button <%= '!rounded-r-none !pr-2' if is_all_completed && !is_combined_enabled %>">
|
<download-button role="button" tabindex="0" aria-label="<%= t('download') %>" data-src="<%= local_assigns[:is_preview] ? (@sig_submitter ? submit_form_documents_path(@sig_submitter.slug, { sig: params[:sig], combined: is_combined_enabled }.compact_blank) : submissions_preview_download_index_path(@submission.slug, combined: is_combined_enabled.presence)) : submission_download_index_path(@submission, combined: is_combined_enabled.presence) %>" class="base-button <%= '!rounded-r-none !pr-2' if show_download_dropdown %>">
|
||||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||||
<%= svg_icon('download', class: 'w-6 h-6') %>
|
<%= svg_icon('download', class: 'w-6 h-6') %>
|
||||||
<span class="hidden md:inline"><%= t('download') %></span>
|
<span class="hidden md:inline"><%= t('download') %></span>
|
||||||
@@ -49,14 +51,15 @@
|
|||||||
<span class="hidden md:inline"><%= t('downloading') %></span>
|
<span class="hidden md:inline"><%= t('downloading') %></span>
|
||||||
</span>
|
</span>
|
||||||
</download-button>
|
</download-button>
|
||||||
<% if is_all_completed && !is_combined_enabled %>
|
<% if show_download_dropdown %>
|
||||||
<div class="dropdown dropdown-end">
|
<div class="dropdown dropdown-end has-[button:disabled]:dropdown-open">
|
||||||
<label tabindex="0" aria-label="<%= t('download') %>" class="base-button !rounded-l-none !pl-1 !pr-2 !border-l-neutral-500">
|
<label tabindex="0" aria-label="<%= t('download') %>" class="base-button !rounded-l-none !pl-1 !pr-2 !border-l-neutral-500">
|
||||||
<span class="text-sm align-text-top">
|
<span class="text-sm align-text-top">
|
||||||
<%= svg_icon('chevron_down', class: 'w-6 h-6 flex-shrink-0 stroke-2') %>
|
<%= svg_icon('chevron_down', class: 'w-6 h-6 flex-shrink-0 stroke-2') %>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<ul class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box text-right">
|
<ul class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box text-right">
|
||||||
|
<% if show_combined_download %>
|
||||||
<li>
|
<li>
|
||||||
<download-button role="button" tabindex="0" data-src="<%= local_assigns[:is_preview] ? (@sig_submitter ? submit_form_documents_path(@sig_submitter.slug, { sig: params[:sig], combined: true }.compact) : submissions_preview_download_index_path(@submission.slug, combined: true)) : submission_download_index_path(@submission, combined: true) %>" class="flex items-center">
|
<download-button role="button" tabindex="0" data-src="<%= local_assigns[:is_preview] ? (@sig_submitter ? submit_form_documents_path(@sig_submitter.slug, { sig: params[:sig], combined: true }.compact) : submissions_preview_download_index_path(@submission.slug, combined: true)) : submission_download_index_path(@submission, combined: true) %>" class="flex items-center">
|
||||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||||
@@ -69,6 +72,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</download-button>
|
</download-button>
|
||||||
</li>
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% if show_unarchive %>
|
||||||
|
<li>
|
||||||
|
<button form="submission_unarchive_form">
|
||||||
|
<%= button_title(title: t('unarchive'), disabled_with: t('unarchive'), icon: svg_icon('rotate', class: 'w-6 h-6 flex-shrink-0')) %>
|
||||||
|
</button>
|
||||||
|
<%= button_to '', submission_unarchive_index_path(@submission), form: { id: 'submission_unarchive_form' }, form_class: 'hidden' %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -83,11 +95,12 @@
|
|||||||
<div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5" tabindex="0">
|
<div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5" tabindex="0">
|
||||||
<% values = @submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
<% values = @submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
||||||
<% schema = Submissions.filtered_conditions_schema(@submission, values:) %>
|
<% schema = Submissions.filtered_conditions_schema(@submission, values:) %>
|
||||||
|
<% service_url_time = ActiveStorage::Attachment.service_url_time %>
|
||||||
<% schema.each do |item| %>
|
<% schema.each do |item| %>
|
||||||
<% document = @submission.schema_documents.find { |a| item['attachment_uuid'] == a.uuid } %>
|
<% document = @submission.schema_documents.find { |a| item['attachment_uuid'] == a.uuid } %>
|
||||||
<% if document.preview_images.first %>
|
<% if document.preview_images.first %>
|
||||||
<scroll-to data-selector-id="page-<%= document.uuid %>-0" class="block cursor-pointer">
|
<scroll-to data-selector-id="page-<%= document.uuid %>-0" class="block cursor-pointer">
|
||||||
<img src="<%= (document.preview_images.find { |e| e.filename.base.to_i.zero? } || document.preview_images.first).url %>" width="<%= document.preview_images.first.metadata['width'] %>" height="<%= document.preview_images.first.metadata['height'] %>" class="rounded border" loading="lazy" alt="<%= item['name'].presence || document.filename.base %>">
|
<img src="<%= (document.preview_images.find { |e| e.filename.base.to_i.zero? } || document.preview_images.first).url(time: service_url_time) %>" width="<%= document.preview_images.first.metadata['width'] %>" height="<%= document.preview_images.first.metadata['height'] %>" class="rounded border" loading="lazy" alt="<%= item['name'].presence || document.filename.base %>">
|
||||||
<div class="pb-2 pt-1.5 text-center" dir="auto">
|
<div class="pb-2 pt-1.5 text-center" dir="auto">
|
||||||
<%= item['name'].presence || document.filename.base %>
|
<%= item['name'].presence || document.filename.base %>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,7 +124,7 @@
|
|||||||
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
|
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
|
||||||
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_key, "#{index}#{Templates::ProcessDocument::PREVIEW_FORMAT}")) %>
|
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_key, "#{index}#{Templates::ProcessDocument::PREVIEW_FORMAT}")) %>
|
||||||
<page-container id="<%= "page-#{document.uuid}-#{index}" %>" class="block before:border before:absolute before:top-0 before:bottom-0 before:left-0 before:right-0 before:rounded relative mb-4" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
|
<page-container id="<%= "page-#{document.uuid}-#{index}" %>" class="block before:border before:absolute before:top-0 before:bottom-0 before:left-0 before:right-0 before:rounded relative mb-4" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
|
||||||
<img loading="lazy" src="<%= page.url %>" width="<%= width %>" class="rounded" height="<%= height %>" alt="<%= "#{item['name']} - #{t('page')} #{index + 1}" %>">
|
<img loading="lazy" src="<%= page.is_a?(ActiveStorage::Attachment) ? page.url(time: service_url_time) : page.url %>" width="<%= width %>" class="rounded" height="<%= height %>" alt="<%= "#{item['name']} - #{t('page')} #{index + 1}" %>">
|
||||||
<div class="top-0 bottom-0 left-0 right-0 absolute">
|
<div class="top-0 bottom-0 left-0 right-0 absolute">
|
||||||
<% document_annots_index[index]&.each do |annot| %>
|
<% document_annots_index[index]&.each do |annot| %>
|
||||||
<%= render 'submissions/annotation', annot: %>
|
<%= render 'submissions/annotation', annot: %>
|
||||||
@@ -231,7 +244,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && !@submission.template&.archived_at? && can?(:update, @submission) && Accounts.can_send_emails?(current_account) && !@submission.expired? && !submitter.declined_at? %>
|
<% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && !@submission.template&.archived_at? && can?(:update, @submission) && (Docuseal.multitenant? || Accounts.can_send_emails?(current_account)) && !@submission.expired? && !submitter.declined_at? %>
|
||||||
<div class="mt-2 mb-1">
|
<div class="mt-2 mb-1">
|
||||||
<%= button_to button_title(title: submitter.sent_at? ? t('re_send_email') : t('send_email'), disabled_with: t('sending')), submitter_send_email_index_path(submitter), class: 'btn btn-sm btn-primary w-full' %>
|
<%= button_to button_title(title: submitter.sent_at? ? t('re_send_email') : t('send_email'), disabled_with: t('sending')), submitter_send_email_index_path(submitter), class: 'btn btn-sm btn-primary w-full' %>
|
||||||
</div>
|
</div>
|
||||||
@@ -267,11 +280,11 @@
|
|||||||
<% if field['type'].in?(%w[signature initials]) %>
|
<% if field['type'].in?(%w[signature initials]) %>
|
||||||
<div class="w-full bg-base-300 py-1">
|
<div class="w-full bg-base-300 py-1">
|
||||||
<% img_height = attachments_index[value].metadata['height'] %>
|
<% img_height = attachments_index[value].metadata['height'] %>
|
||||||
<img class="object-contain mx-auto" style="<%= 'max-' if img_height %>height: <%= field['type'] == 'signature' ? 100 : 50 %>px" height="<%= img_height %>" width="<%= attachments_index[value].metadata['width'] %>" src="<%= attachments_index[value].url %>" loading="lazy" alt="<%= field['name'] || field['title'] || field['type'] %>">
|
<img class="object-contain mx-auto" style="<%= 'max-' if img_height %>height: <%= field['type'] == 'signature' ? 100 : 50 %>px" height="<%= img_height %>" width="<%= attachments_index[value].metadata['width'] %>" src="<%= attachments_index[value].url(time: service_url_time) %>" loading="lazy" alt="<%= field['name'] || field['title'] || field['type'] %>">
|
||||||
</div>
|
</div>
|
||||||
<% elsif field['type'].in?(['image', 'stamp', 'kba']) && attachments_index[value].image? %>
|
<% elsif field['type'].in?(['image', 'stamp', 'kba']) && attachments_index[value].image? %>
|
||||||
<% img_height = attachments_index[value].metadata['height'] %>
|
<% img_height = attachments_index[value].metadata['height'] %>
|
||||||
<img class="object-contain mx-auto" style="<%= 'max-' if img_height %>height: 200px" height="<%= img_height %>" width="<%= attachments_index[value].metadata['width'] %>" src="<%= attachments_index[value].url %>" loading="lazy" alt="<%= field['name'] || field['title'] || field['type'] %>">
|
<img class="object-contain mx-auto" style="<%= 'max-' if img_height %>height: 200px" height="<%= img_height %>" width="<%= attachments_index[value].metadata['width'] %>" src="<%= attachments_index[value].url(time: service_url_time) %>" loading="lazy" alt="<%= field['name'] || field['title'] || field['type'] %>">
|
||||||
<% elsif field['type'].in?(['file', 'payment', 'image']) %>
|
<% elsif field['type'].in?(['file', 'payment', 'image']) %>
|
||||||
<div class="flex flex-col justify-center">
|
<div class="flex flex-col justify-center">
|
||||||
<% Array.wrap(value).each do |val| %>
|
<% Array.wrap(value).each do |val| %>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p dir="auto" class="text-lg font-bold mb-1"><%= @submission.name || @submission.template.name %></p>
|
<p dir="auto" class="text-lg font-bold mb-1"><%= @submission.name || @submission.template&.name %></p>
|
||||||
<% last_submitter = @submission.submitters.completed.order(:completed_at).last %>
|
<% last_submitter = @submission.submitters.completed.order(:completed_at).last %>
|
||||||
<% if last_submitter %>
|
<% if last_submitter %>
|
||||||
<p dir="auto" class="text-sm">
|
<p dir="auto" class="text-sm">
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
<% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %>
|
<% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %>
|
||||||
<% 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 %>
|
<% 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 %>
|
||||||
<% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
<% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
||||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-field-labels="<%= configs[:with_field_labels] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template&.preferences&.dig('completed_redirect_url') %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template&.preferences&.dig('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-optional-invite-submitters="<%= optional_invite_submitters %>" 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-signature-text="<%= params[: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] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>
|
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-field-labels="<%= configs[:with_field_labels] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template&.preferences&.dig('completed_redirect_url') %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template&.preferences&.dig('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="<%= Docuseal.multitenant? || Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" 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-signature-text="<%= params[: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] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<p class="text-sm"><%= t('form_has_been_deleted_by_html', name: @submitter.account.name) %></p>
|
<p class="text-sm"><%= t('form_has_been_deleted_by_html', name: @submitter.account.name) %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<p class="text-sm"><%= t('awaiting_completion_by_the_other_party') %></p>
|
<p class="text-sm"><%= t('awaiting_completion_by_the_other_party') %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p dir="auto" class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p dir="auto" class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<p dir="auto" class="text-sm">
|
<p dir="auto" class="text-sm">
|
||||||
<%= t(@submitter.with_signature_fields? ? 'signed_on_time' : 'completed_on_time', time: l(@submitter.completed_at.to_date, format: :long)) %>
|
<%= t(@submitter.with_signature_fields? ? 'signed_on_time' : 'completed_on_time', time: l(@submitter.completed_at.to_date, format: :long)) %>
|
||||||
</p>
|
</p>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% undefined_submitters = Templates.filter_undefined_submitters(@submitter.submission.template_submitters) %>
|
<% undefined_submitters = Templates.filter_undefined_submitters(@submitter.submission.template_submitters) %>
|
||||||
<% if undefined_submitters.size == 1 && undefined_submitters.first['uuid'] == @submitter.uuid && %w[api embed].exclude?(@submitter.submission.source) && @submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false && @submitter.template && !@submitter.template.archived_at? %>
|
<% if undefined_submitters.size == 1 && undefined_submitters.first['uuid'] == @submitter.uuid && @submitter.completed_at? && @submitter.completed_at > 14.days.ago && %w[api embed mcp].exclude?(@submitter.submission.source) && @submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false && @submitter.template && !@submitter.template.archived_at? %>
|
||||||
<div class="divider uppercase"><%= t('or') %></div>
|
<div class="divider uppercase"><%= t('or') %></div>
|
||||||
<toggle-submit class="block">
|
<toggle-submit class="block">
|
||||||
<%= button_to button_title(title: t('resubmit'), disabled_with: t('resubmit'), icon: svg_icon('reload', class: 'w-6 h-6')), resubmit_form_path, params: { resubmit: @submitter.slug }, method: :put, class: 'white-button w-full' %>
|
<%= button_to button_title(title: t('resubmit'), disabled_with: t('resubmit'), icon: svg_icon('reload', class: 'w-6 h-6')), resubmit_form_path, params: { resubmit: @submitter.slug }, method: :put, class: 'white-button w-full' %>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<p class="text-sm"><%= t('form_has_been_declined_on_html', time: l(@submitter.declined_at, format: :long)) %></p>
|
<p class="text-sm"><%= t('form_has_been_declined_on_html', time: l(@submitter.declined_at, format: :long)) %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('user_share', class: 'w-10 h-10') %>
|
<%= svg_icon('user_share', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<p class="text-sm"><%= t('document_has_been_delegated_on_html', time: l(@submitter.submission_events.order(event_timestamp: :desc).find_by!(event_type: :delegate_form).event_timestamp, format: :long)) %></p>
|
<p class="text-sm"><%= t('document_has_been_delegated_on_html', time: l(@submitter.submission_events.order(event_timestamp: :desc).find_by!(event_type: :delegate_form).event_timestamp, format: :long)) %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<% content_for(:html_title, "#{@submitter.submission.name || @submitter.submission.template.name} | DocuSeal") %>
|
<% content_for(:html_title, "#{@submitter.submission.name || @submitter.submission.template&.name} | DocuSeal") %>
|
||||||
<% I18n.with_locale(@submitter.account.locale) do %>
|
<% I18n.with_locale(@submitter.account.locale) do %>
|
||||||
<% content_for(:html_description, t('account_name_has_invited_you_to_fill_and_sign_documents_online_effortlessly_with_a_secure_fast_and_user_friendly_digital_document_signing_solution', account_name: @submitter.account.name)) %>
|
<% content_for(:html_description, t('account_name_has_invited_you_to_fill_and_sign_documents_online_effortlessly_with_a_secure_fast_and_user_friendly_digital_document_signing_solution', account_name: @submitter.account.name)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p dir="auto" class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p dir="auto" class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<% last_submitter = @submitter.submission.submitters.completed.order(:completed_at).last %>
|
<% last_submitter = @submitter.submission.submitters.completed.order(:completed_at).last %>
|
||||||
<% if last_submitter %>
|
<% if last_submitter %>
|
||||||
<p dir="auto" class="text-sm">
|
<p dir="auto" class="text-sm">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template.name %></p>
|
<p class="text-lg font-bold mb-1"><%= @submitter.submission.name || @submitter.submission.template&.name %></p>
|
||||||
<p class="text-sm"><%= t('form_expired_at_html', time: l(@submitter.submission.expire_at, format: :long)) %></p>
|
<p class="text-sm"><%= t('form_expired_at_html', time: l(@submitter.submission.expire_at, format: :long)) %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<% content_for(:html_title, "#{@submitter.submission.name || @submitter.submission.template.name} | DocuSeal") %>
|
<% content_for(:html_title, "#{@submitter.submission.name || @submitter.submission.template&.name} | DocuSeal") %>
|
||||||
<% I18n.with_locale(@submitter.account.locale) do %>
|
<% I18n.with_locale(@submitter.account.locale) do %>
|
||||||
<% content_for(:html_description, t('account_name_has_invited_you_to_fill_and_sign_documents_online_effortlessly_with_a_secure_fast_and_user_friendly_digital_document_signing_solution', account_name: @submitter.account.name)) %>
|
<% content_for(:html_description, t('account_name_has_invited_you_to_fill_and_sign_documents_online_effortlessly_with_a_secure_fast_and_user_friendly_digital_document_signing_solution', account_name: @submitter.account.name)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<% values = @submitter.submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
<% values = @submitter.submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
||||||
<% submitters_index = @submitter.submission.submitters.index_by(&:uuid) %>
|
<% submitters_index = @submitter.submission.submitters.index_by(&:uuid) %>
|
||||||
<% page_blob_struct = Struct.new(:url, :metadata) %>
|
<% page_blob_struct = Struct.new(:url, :metadata) %>
|
||||||
|
<% service_url_time = ActiveStorage::Attachment.service_url_time %>
|
||||||
<% schema = Submissions.filtered_conditions_schema(@submitter.submission, values:, include_submitter_uuid: @submitter.uuid) %>
|
<% schema = Submissions.filtered_conditions_schema(@submitter.submission, values:, include_submitter_uuid: @submitter.uuid) %>
|
||||||
<% font_scale = 1000.0 / PdfUtils::US_LETTER_W %>
|
<% font_scale = 1000.0 / PdfUtils::US_LETTER_W %>
|
||||||
<% decline_modal_id = nil %>
|
<% decline_modal_id = nil %>
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
<%= render('submit_form/banner') %>
|
<%= render('submit_form/banner') %>
|
||||||
<header id="signing_form_header" class="sticky min-[1230px]:static top-0 z-50 bg-base-100 py-2 px-2 flex items-center md:-mx-[8px]" style="margin-bottom: -16px">
|
<header id="signing_form_header" class="sticky min-[1230px]:static top-0 z-50 bg-base-100 py-2 px-2 flex items-center md:-mx-[8px]" style="margin-bottom: -16px">
|
||||||
<h1 class="text-xl md:text-2xl font-medium focus:text-clip" style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
<h1 class="text-xl md:text-2xl font-medium focus:text-clip" style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
||||||
<%= @submitter.submission.name || @submitter.submission.template.name %>
|
<%= @submitter.submission.name || @submitter.submission.template&.name %>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex items-center gap-2 group" style="margin-left: 20px; flex-shrink: 0">
|
<div class="flex items-center gap-2 group" style="margin-left: 20px; flex-shrink: 0">
|
||||||
<% if @form_configs[:with_decline] %>
|
<% if @form_configs[:with_decline] %>
|
||||||
@@ -138,7 +139,7 @@
|
|||||||
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
|
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
|
||||||
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_key, "#{index}#{Templates::ProcessDocument::PREVIEW_FORMAT}")) %>
|
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_key, "#{index}#{Templates::ProcessDocument::PREVIEW_FORMAT}")) %>
|
||||||
<page-container class="block relative my-4 shadow-md" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
|
<page-container class="block relative my-4 shadow-md" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
|
||||||
<img loading="lazy" src="<%= page.url %>" width="<%= width %>" height="<%= height %>" alt="<%= "#{item['name']} - #{t('page')} #{index + 1}" %>">
|
<img loading="lazy" src="<%= page.is_a?(ActiveStorage::Attachment) ? page.url(time: service_url_time) : page.url %>" width="<%= width %>" height="<%= height %>" alt="<%= "#{item['name']} - #{t('page')} #{index + 1}" %>">
|
||||||
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
|
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
|
||||||
<% if annots = document_annots_index[index] %>
|
<% if annots = document_annots_index[index] %>
|
||||||
<%= render 'submit_form/annotations', annots: %>
|
<%= render 'submit_form/annotations', annots: %>
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
<%= render 'custom_content', content: @body, submitter: @submitter %>
|
<%= render 'custom_content', content: @body, submitter: @submitter %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p><%= t('hi_there') %>,</p>
|
<p><%= t('hi_there') %>,</p>
|
||||||
<p><%= I18n.t(:name_has_been_completed_by_submitters, name: @submitter.submission.name || @submitter.submission.template.name, submitters: @submitter.submission.submitters.order(:completed_at).map { |e| e.name || e.email || e.phone }.uniq.join(', ')) %></p>
|
<p><%= I18n.t(:name_has_been_completed_by_submitters, name: @submitter.submission.name || @submitter.submission.template&.name, submitters: @submitter.submission.submitters.order(:completed_at).map { |e| e.name || e.email || e.phone }.uniq.join(', ')) %></p>
|
||||||
<p><%= link_to submission_url(@submitter.submission), submission_url(@submitter.submission) %></p>
|
<p><%= link_to submission_url(@submitter.submission), submission_url(@submitter.submission) %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<p><%= t('hi_there') %>,</p>
|
<p><%= t('hi_there') %>,</p>
|
||||||
<p><%= t('name_declined_by_submitter_with_the_following_reason', name: @submitter.submission.name || @submitter.submission.template.name, submitter: @submitter.name || @submitter.email || @submitter.phone) %></p>
|
<p><%= t('name_declined_by_submitter_with_the_following_reason', name: @submitter.submission.name || @submitter.submission.template&.name, submitter: @submitter.name || @submitter.email || @submitter.phone) %></p>
|
||||||
<%= simple_format(h(@submitter.submission_events.find_by(event_type: :decline_form).data['reason']), {}, sanitize: false) %>
|
<%= simple_format(h(@submitter.submission_events.find_by(event_type: :decline_form).data['reason']), {}, sanitize: false) %>
|
||||||
<p><%= link_to submission_url(@submitter.submission), submission_url(@submitter.submission) %></p>
|
<p><%= link_to submission_url(@submitter.submission), submission_url(@submitter.submission) %></p>
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
<%= render 'custom_content', content: @body, submitter: @submitter, sig: @sig %>
|
<%= render 'custom_content', content: @body, submitter: @submitter, sig: @sig %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p><%= t('hi_there') %>,</p>
|
<p><%= t('hi_there') %>,</p>
|
||||||
<p><%= t('please_check_the_copy_of_your_name_in_the_email_attachments', name: @submitter.submission.name || @submitter.submission.template.name) %>
|
<p><%= t('please_check_the_copy_of_your_name_in_the_email_attachments', name: @submitter.submission.name || @submitter.submission.template&.name) %>
|
||||||
<p><%= t('alternatively_you_can_review_and_download_your_copy_using_the_link_below') %></p>
|
<p><%= t('alternatively_you_can_review_and_download_your_copy_using_the_link_below') %></p>
|
||||||
<p>
|
<p>
|
||||||
<%= link_to @submitter.submission.name || @submitter.submission.template.name, submissions_preview_url(@submitter.submission.slug, { sig: @sig, host: @custom_domain || ENV.fetch('EMAIL_HOST', Docuseal.default_url_options[:host]) }.compact) %>
|
<%= link_to @submitter.submission.name || @submitter.submission.template&.name, submissions_preview_url(@submitter.submission.slug, { sig: @sig, host: @custom_domain || ENV.fetch('EMAIL_HOST', Docuseal.default_url_options[:host]) }.compact) %>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<%= t('thanks') %>,<br><%= @current_account.name %>
|
<%= t('thanks') %>,<br><%= @current_account.name %>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p><%= t('hi_there') %>,</p>
|
<p><%= t('hi_there') %>,</p>
|
||||||
<p><%= I18n.t(@submitter.with_signature_fields? ? :you_have_been_invited_to_sign_the_name : :you_have_been_invited_to_submit_the_name_form, name: @submitter.submission.name || @submitter.submission.template.name) %></p>
|
<p><%= I18n.t(@submitter.with_signature_fields? ? :you_have_been_invited_to_sign_the_name : :you_have_been_invited_to_submit_the_name_form, name: @submitter.submission.name || @submitter.submission.template&.name) %></p>
|
||||||
<p><%= link_to I18n.t(@submitter.with_signature_fields? ? :review_and_sign : :review_and_submit), submit_form_url(slug: @submitter.slug, t: SubmissionEvents.build_tracking_param(@submitter, 'click_email'), host: @custom_domain || ENV.fetch('EMAIL_HOST', Docuseal.default_url_options[:host])) %></p>
|
<p><%= link_to I18n.t(@submitter.with_signature_fields? ? :review_and_sign : :review_and_submit), submit_form_url(slug: @submitter.slug, t: SubmissionEvents.build_tracking_param(@submitter, 'click_email'), host: @custom_domain || ENV.fetch('EMAIL_HOST', Docuseal.default_url_options[:host])) %></p>
|
||||||
<p><%= t('please_contact_us_by_replying_to_this_email_if_you_have_any_questions') %></p>
|
<p><%= t('please_contact_us_by_replying_to_this_email_if_you_have_any_questions') %></p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<p><%= t('your_verification_code_to_access_the_name', name: @submitter.submission.name || @submitter.submission.template.name) %></p>
|
<p><%= t('your_verification_code_to_access_the_name', name: @submitter.submission.name || @submitter.submission.template&.name) %></p>
|
||||||
<p><b><%= @otp_code %></b></p>
|
<p><b><%= @otp_code %></b></p>
|
||||||
<p><%= t('please_reply_to_this_email_if_you_didnt_request_this') %></p>
|
<p><%= t('please_reply_to_this_email_if_you_didnt_request_this') %></p>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
</submitter-item>
|
</submitter-item>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<%= 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 %>
|
<%= 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, can_send_emails: Accounts.can_send_emails?(current_account) %>
|
||||||
<%= render 'submissions/send_sms', f:, resend_sms: @submitter.sent_at? %>
|
<%= render 'submissions/send_sms', f:, resend_sms: @submitter.sent_at? %>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control mt-4">
|
<div class="form-control mt-4">
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<% is_long = folder.name.size > 32 %>
|
<% is_long = folder.name.size > 32 %>
|
||||||
|
<% icon = folder.default? ? 'folder_star' : 'folder' %>
|
||||||
<a href="<%= folder_path(folder) %>" class="flex h-full flex-col justify-between rounded-2xl py-5 px-6 w-full bg-base-200 before:border-2 before:border-base-300 before:border-dashed before:absolute before:left-0 before:right-0 before:top-0 before:bottom-0 before:hidden before:rounded-2xl relative" data-targets="dashboard-dropzone.folderCards" data-full-name="<%= folder.full_name %>">
|
<a href="<%= folder_path(folder) %>" class="flex h-full flex-col justify-between rounded-2xl py-5 px-6 w-full bg-base-200 before:border-2 before:border-base-300 before:border-dashed before:absolute before:left-0 before:right-0 before:top-0 before:bottom-0 before:hidden before:rounded-2xl relative" data-targets="dashboard-dropzone.folderCards" data-full-name="<%= folder.full_name %>">
|
||||||
<% if !is_long %>
|
<% if !is_long %>
|
||||||
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
<%= svg_icon(icon, class: 'w-6 h-6') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="text-lg font-semibold mt-1" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: <%= is_long ? 2 : 1 %>;">
|
<div class="text-lg font-semibold mt-1" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: <%= is_long ? 2 : 1 %>;">
|
||||||
<% if is_long %>
|
<% if is_long %>
|
||||||
<%= svg_icon('folder', class: 'w-6 h-6 inline') %>
|
<%= svg_icon(icon, class: 'w-6 h-6 inline') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= folder.name %>
|
<%= folder.name %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<a href="<%= templates_shared_index_path %>" class="flex h-full flex-col justify-between rounded-2xl py-5 px-6 w-full bg-base-200 before:border-2 before:border-base-300 before:border-dashed before:absolute before:left-0 before:right-0 before:top-0 before:bottom-0 before:hidden before:rounded-2xl relative">
|
||||||
|
<%= svg_icon('folder_up', class: 'w-6 h-6') %>
|
||||||
|
<div class="text-lg font-semibold mt-1 capitalize" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;">
|
||||||
|
<%= t('shared') %>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
<% if params[:q].present? || @pagy.count.nil? || @pagy.count > 0 || @template_folders.present? %>
|
||||||
<%= render 'shared/search_input' %>
|
<%= render 'shared/search_input' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if can?(:create, ::Template) %>
|
<% if can?(:create, ::Template) %>
|
||||||
@@ -54,8 +54,8 @@
|
|||||||
<%= render partial: 'templates/template', collection: @templates %>
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
</div>
|
</div>
|
||||||
<% templates_order_select_html = capture do %>
|
<% templates_order_select_html = capture do %>
|
||||||
<% if params[:q].blank? && @pagy.pages > 1 %>
|
<% if params[:q].blank? && @pagy.pages > 1 && !can?(:manage, :countless) %>
|
||||||
<%= render 'shared/templates_order_select', with_recently_used: @pagy.count.present? && @pagy.count < 10_000 && !can?(:manage, :countless), selected_order: %>
|
<%= render 'shared/templates_order_select', with_recently_used: @pagy.count.present? && @pagy.count < 10_000, selected_order: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'shared/pagination', pagy: @pagy, items_name: @templates.present? ? 'templates' : 'template_folders', right_additional_html: templates_order_select_html %>
|
<%= render 'shared/pagination', pagy: @pagy, items_name: @templates.present? ? 'templates' : 'template_folders', right_additional_html: templates_order_select_html %>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="font-medium items-start w-full group-hover:link text-sm flex space-x-1">
|
<div class="font-medium items-start w-full group-hover:link text-sm flex space-x-1">
|
||||||
<%= svg_icon('file_text', class: 'w-4 h-4 mt-0.5 flex-shrink-0') %>
|
<%= svg_icon('file_text', class: 'w-4 h-4 mt-0.5 flex-shrink-0') %>
|
||||||
<span style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
|
<span style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
|
||||||
<% (submission.name || template.name).split(/(_)/).each do |item| %><%= item %><wbr><% end %>
|
<% (submission.name || template&.name).to_s.split(/(_)/).each do |item| %><%= item %><wbr><% end %>
|
||||||
<%= svg_icon('arrow_right', class: 'w-4 h-4 sm:inline group-hover:visible invisible hidden') %>
|
<%= svg_icon('arrow_right', class: 'w-4 h-4 sm:inline group-hover:visible invisible hidden') %>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<div class="w-full flex flex-col md:flex-row space-y-4 md:space-y-0 md:justify-between px-5 md:px-6 pb-5 md:items-center pt-5 relative cursor-pointer">
|
<div class="w-full flex flex-col md:flex-row space-y-4 md:space-y-0 md:justify-between px-5 md:px-6 pb-5 md:items-center pt-5 relative cursor-pointer">
|
||||||
<% submitters = (submission.template_submitters || submission.template.submitters).filter_map { |item| submission.submitters.find { |e| e.uuid == item['uuid'] } } %>
|
<% 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?) && submitters.size.positive? %>
|
<% is_submission_completed = submission.completed_at? %>
|
||||||
<% if submitters.size == 1 %>
|
<% if submitters.size == 1 %>
|
||||||
<div>
|
<div>
|
||||||
<% submitter = submitters.first %>
|
<% submitter = submitters.first %>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<a href="<%= submission_path(submission) %>" class="text-lg break-all peer">
|
<a href="<%= submission_path(submission) %>" class="text-lg break-all peer">
|
||||||
<%= submitter.name || submitter.email || submitter.phone %>
|
<%= submitter.name || submitter.email || submitter.phone %>
|
||||||
</a>
|
</a>
|
||||||
<% if can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %>
|
<% if !submitter.completed_at? && can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %>
|
||||||
<span class="pl-0.5 tooltip tooltip-top md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>">
|
<span class="pl-0.5 tooltip tooltip-top md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>">
|
||||||
<%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %>
|
<%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %>
|
||||||
<%= svg_icon('pencil', class: 'w-5 h-5') %>
|
<%= svg_icon('pencil', class: 'w-5 h-5') %>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
<a href="<%= submission_path(submission) %>" class="text-lg break-all peer">
|
<a href="<%= submission_path(submission) %>" class="text-lg break-all peer">
|
||||||
<%= submitter.name || submitter.email || submitter.phone %>
|
<%= submitter.name || submitter.email || submitter.phone %>
|
||||||
</a>
|
</a>
|
||||||
<% if can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %>
|
<% if !submitter.completed_at? && can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %>
|
||||||
<span class="pl-0.5 tooltip tooltip-top md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>">
|
<span class="pl-0.5 tooltip tooltip-top md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>">
|
||||||
<%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %>
|
<%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %>
|
||||||
<%= svg_icon('pencil', class: 'w-5 h-5') %>
|
<%= svg_icon('pencil', class: 'w-5 h-5') %>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<%= svg_icon('user', class: 'w-4 h-4') %>
|
<%= svg_icon('user', class: 'w-4 h-4') %>
|
||||||
<span><%= template.author.full_name.presence || template.author.email.to_s.sub(/\+\w+@/, '@') %></span>
|
<span><%= template.author.full_name.presence || template.author.email.to_s.sub(/\+\w+@/, '@') %></span>
|
||||||
<% if template.account_id != current_account.id %>
|
<% if template.account_id != current_account.id %>
|
||||||
<span class="badge badge-neutral badge-outline badge-sm text-[10px] uppercase"><%= t('shared') %></span>
|
<span class="badge badge-neutral badge-outline badge-sm text-[10px] uppercase !border-base-content/60"><%= t('shared') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
<p class="flex text-xs text-base-content/60">
|
<p class="flex text-xs text-base-content/60">
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<%= svg_icon('calendar', class: 'w-4 h-4') %>
|
<%= svg_icon('calendar', class: 'w-4 h-4') %>
|
||||||
<span><%= l(template.created_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %></span>
|
<span><%= l(template.created_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %></span>
|
||||||
</span>
|
</span>
|
||||||
<% if template.archived_at? %>
|
<% if local_assigns[:with_folder] %>
|
||||||
<span class="flex items-center space-x-1 w-1/2">
|
<span class="flex items-center space-x-1 w-1/2">
|
||||||
<%= svg_icon('folder', class: 'w-4 h-4 flex-shrink-0') %>
|
<%= svg_icon('folder', class: 'w-4 h-4 flex-shrink-0') %>
|
||||||
<span class="truncate"><%= template.folder.full_name %></span>
|
<span class="truncate"><%= template.folder.full_name %></span>
|
||||||
@@ -37,14 +37,14 @@
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if template.archived_at? && can?(:update, template) %>
|
<% if template.archived_at? && can?(:destroy, template) %>
|
||||||
<span class="tooltip tooltip-left" data-tip="<%= t('restore') %>">
|
<span class="tooltip tooltip-left" data-tip="<%= t('restore') %>">
|
||||||
<%= button_to template_restore_index_path(template), class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle' do %>
|
<%= button_to template_restore_index_path(template), class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle' do %>
|
||||||
<%= svg_icon('rotate', class: 'w-4 h-4 enabled') %>
|
<%= svg_icon('rotate', class: 'w-4 h-4 enabled') %>
|
||||||
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
|
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</span>
|
</span>
|
||||||
<% elsif can?(:update, template) %>
|
<% elsif !template.archived_at? && can?(:update, template) %>
|
||||||
<span class="tooltip tooltip-left" data-tip="<%= t('edit') %>">
|
<span class="tooltip tooltip-left" data-tip="<%= t('edit') %>">
|
||||||
<a href="<%= edit_template_path(template) %>" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
|
<a href="<%= edit_template_path(template) %>" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
|
||||||
<%= svg_icon('pencil', class: 'w-4 h-4') %>
|
<%= svg_icon('pencil', class: 'w-4 h-4') %>
|
||||||
|
|||||||
@@ -31,9 +31,12 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex items-center space-x-1 mt-1 peer">
|
<a href="<%= templates_shared_index_path %>" class="flex items-center space-x-1 mt-1">
|
||||||
<span class="badge badge-neutral badge-outline badge-md text-xs text-white uppercase"><%= t('shared') %></span>
|
<%= svg_icon('folder', class: 'w-5 h-5 flex-shrink-0') %>
|
||||||
</div>
|
<span class="text-sm capitalize">
|
||||||
|
<%= t('shared') %>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -88,7 +91,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if template.archived_at? %>
|
<% if template.archived_at? %>
|
||||||
<% if can?(:create, template) %>
|
<% if can?(:destroy, template) %>
|
||||||
<%= button_to button_title(title: t('restore'), disabled_with: t('restoring')[..-4], icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm flex-1' %>
|
<%= button_to button_title(title: t('restore'), disabled_with: t('restoring')[..-4], icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm flex-1' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm flex-1' do %>
|
<%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm flex-1' do %>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% unless can?(:manage, :countless) %>
|
<% unless can?(:manage, :countless) %>
|
||||||
<div class="badge badge-neutral badge-outline font-medium">
|
<div class="badge badge-neutral badge-outline font-medium">
|
||||||
<%= params[:status].blank? && filter_params.blank? ? @pagy.count : @base_submissions.unscope(:group, :order).select(:id).distinct.count %>
|
<%= params[:status].blank? && filter_params.blank? ? @pagy.count : @base_submissions.count %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</a>
|
</a>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% unless can?(:manage, :countless) %>
|
<% unless can?(:manage, :countless) %>
|
||||||
<div class="badge badge-neutral badge-outline font-medium">
|
<div class="badge badge-neutral badge-outline font-medium">
|
||||||
<%= params[:status] == 'pending' && filter_params.blank? ? @pagy.count : @base_submissions.pending.unscope(:group, :order).select(:id).distinct.count %>
|
<%= params[:status] == 'pending' && filter_params.blank? ? @pagy.count : @base_submissions.pending.count %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</a>
|
</a>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% unless can?(:manage, :countless) %>
|
<% unless can?(:manage, :countless) %>
|
||||||
<div class="badge badge-neutral badge-outline font-medium">
|
<div class="badge badge-neutral badge-outline font-medium">
|
||||||
<%= params[:status] == 'completed' && filter_params.blank? ? @pagy.count : @base_submissions.completed.unscope(:group, :order).select(:id).distinct.count %>
|
<%= params[:status] == 'completed' && filter_params.blank? ? @pagy.count : @base_submissions.completed.count %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% if @pagy.count.nil? || @pagy.count > 0 %>
|
<% if @pagy.count.nil? || @pagy.count > 0 %>
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
<%= render partial: 'templates/template', collection: @templates %>
|
<%= render partial: 'templates/template', collection: @templates, locals: { with_folder: true } %>
|
||||||
</div>
|
</div>
|
||||||
<% elsif params[:q].present? %>
|
<% elsif params[:q].present? %>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
<% if params[:q].present? || @pagy.count.nil? || @pagy.count > 1 || @template_folders.present? %>
|
||||||
<%= render 'shared/search_input' %>
|
<%= render 'shared/search_input' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if can?(:create, ::Template) %>
|
<% if can?(:create, ::Template) %>
|
||||||
@@ -45,12 +45,18 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% templates_order_select_html = capture do %>
|
<% templates_order_select_html = capture do %>
|
||||||
<% if params[:q].blank? && @pagy.pages > 1 %>
|
<% if params[:q].blank? && @pagy.pages > 1 && !can?(:manage, :countless) %>
|
||||||
<%= render 'shared/templates_order_select', with_recently_used: @pagy.count.present? && @pagy.count < 10_000 && !can?(:manage, :countless), selected_order: %>
|
<%= render 'shared/templates_order_select', with_recently_used: @pagy.count.present? && @pagy.count < 10_000, selected_order: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if @template_folders.present? %>
|
<% if @template_folders.present? || @show_default_folder || @show_shared_folder %>
|
||||||
<div class="grid gap-4 md:grid-cols-3 <%= 'mb-6' if @templates.present? %>">
|
<div class="grid gap-4 md:grid-cols-3 <%= 'mb-6' if @templates.present? %>">
|
||||||
|
<% if @show_default_folder %>
|
||||||
|
<%= render 'template_folders/folder', folder: @default_folder %>
|
||||||
|
<% end %>
|
||||||
|
<% if @show_shared_folder %>
|
||||||
|
<%= render 'template_folders/shared_folder' %>
|
||||||
|
<% end %>
|
||||||
<%= render partial: 'template_folders/folder', collection: @template_folders, as: :folder %>
|
<%= render partial: 'template_folders/folder', collection: @template_folders, as: :folder %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<div>
|
||||||
|
<%= link_to(@is_archived ? templates_shared_index_path : root_path, class: 'flex items-center') do %>
|
||||||
|
<%= svg_icon('chevron_left', class: 'w-5 h-5') %>
|
||||||
|
<span style="margin-left: 3px"><%= @is_archived ? t('back_to_active') : t('home') %></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="relative flex justify-between items-center w-full mb-4">
|
||||||
|
<h1 class="text-2xl truncate md:text-3xl font-bold flex items-center flex-grow min-w-0 space-x-2 md:flex <%= 'hidden' if params[:q].present? %>">
|
||||||
|
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
||||||
|
<span class="truncate capitalize"><%= t('shared') %></span>
|
||||||
|
<% if @is_archived %>
|
||||||
|
<span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span>
|
||||||
|
<% end %>
|
||||||
|
</h1>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<% if params[:q].present? || @pagy.count.nil? || @pagy.count > 1 %>
|
||||||
|
<%= render 'shared/search_input' %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% view_archived_html = capture do %>
|
||||||
|
<% if @has_archived && params[:q].blank? %>
|
||||||
|
<div>
|
||||||
|
<a href="<%= templates_shared_index_path(archived: true) %>" class="link text-sm"><%= t('view_archived') %></a>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% if @pagy.count.nil? || @pagy.count > 0 %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
|
</div>
|
||||||
|
<% elsif params[:q].present? %>
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="mt-16 text-3xl font-semibold">
|
||||||
|
<%= t('templates_not_found') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% if @related_submissions.present? %>
|
||||||
|
<h1 class="text-2xl md:text-3xl sm:text-4xl font-bold mt-8 md:mt-4">
|
||||||
|
<%= t('submissions') %>
|
||||||
|
</h1>
|
||||||
|
<div class="space-y-4 mt-4">
|
||||||
|
<%= render partial: 'templates/submission', collection: @related_submissions, locals: { with_template: true } %>
|
||||||
|
</div>
|
||||||
|
<%= render 'shared/pagination', pagy: @related_submissions_pagy, items_name: 'submissions', next_page_path: @is_archived ? submissions_archived_index_path(q: params[:q]) : submissions_path(q: params[:q]) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% if @pagy.pages > 1 %>
|
||||||
|
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates', left_additional_html: view_archived_html %>
|
||||||
|
<% else %>
|
||||||
|
<div class="mt-2">
|
||||||
|
<%= view_archived_html %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
@@ -1,3 +1,22 @@
|
|||||||
|
<% if Addressable::URI.parse(params[:url]).host == 'docuseal-website.s3.amazonaws.com' %>
|
||||||
|
<div class="h-screen">
|
||||||
|
<div class="text-center p-8 h-full flex items-center justify-center">
|
||||||
|
<div>
|
||||||
|
<%= render 'shared/logo', width: 50, height: 50, class: 'mx-auto animate-bounce' %>
|
||||||
|
<span>
|
||||||
|
<%= t('processing') %>...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<submit-form>
|
||||||
|
<%= form_for '', url: templates_upload_path, method: :post, class: 'hidden' do %>
|
||||||
|
<button type="submit"></button>
|
||||||
|
<input name="url" value="<%= params[:url] %>">
|
||||||
|
<input name="filename" value="<%= params[:filename] %>">
|
||||||
|
<% end %>
|
||||||
|
</submit-form>
|
||||||
|
<% else %>
|
||||||
<confirm-upload>
|
<confirm-upload>
|
||||||
<div class="h-screen">
|
<div class="h-screen">
|
||||||
<div class="text-center p-8 h-full flex items-center justify-center">
|
<div class="text-center p-8 h-full flex items-center justify-center">
|
||||||
@@ -28,3 +47,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</confirm-upload>
|
</confirm-upload>
|
||||||
|
<% end %>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<span class="label-text-alt"><%= t('email_address_is_awaiting_confirmation_follow_the_link_in_the_email_to_confirm', email: f.object.unconfirmed_email) %></span>
|
<span class="label-text-alt"><%= t('email_address_is_awaiting_confirmation_follow_the_link_in_the_email_to_confirm', email: f.object.unconfirmed_email) %></span>
|
||||||
</label>
|
</label>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if user.persisted? && Accounts.can_send_emails?(current_account) %>
|
<% if user.persisted? && (Docuseal.multitenant? || Accounts.can_send_emails?(current_account)) %>
|
||||||
<span class="label-text-alt mt-2 mx-1">
|
<span class="label-text-alt mt-2 mx-1">
|
||||||
<%= t('click_here_to_send_a_reset_password_email_html') %>
|
<%= t('click_here_to_send_a_reset_password_email_html') %>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ module DocuSeal
|
|||||||
|
|
||||||
config.active_storage.analyzers = []
|
config.active_storage.analyzers = []
|
||||||
|
|
||||||
|
config.active_storage.previewers = []
|
||||||
|
|
||||||
config.active_storage.variant_processor = :disabled
|
config.active_storage.variant_processor = :disabled
|
||||||
|
|
||||||
config.active_storage.content_types_to_serve_as_binary += %w[
|
config.active_storage.content_types_to_serve_as_binary += %w[
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ ActiveSupport.on_load(:active_storage_attachment) do
|
|||||||
|
|
||||||
has_many_attached :preview_images
|
has_many_attached :preview_images
|
||||||
|
|
||||||
|
def self.service_url_time
|
||||||
|
return unless Docuseal.multitenant?
|
||||||
|
|
||||||
|
now = Time.current
|
||||||
|
|
||||||
|
now.min < 30 ? now.beginning_of_hour : now.beginning_of_hour + 30.minutes
|
||||||
|
end
|
||||||
|
|
||||||
def signed_uuid
|
def signed_uuid
|
||||||
@signed_uuid ||= ApplicationRecord.signed_id_verifier.generate(uuid, expires_in: 6.hours, purpose: :attachment)
|
@signed_uuid ||= ApplicationRecord.signed_id_verifier.generate(uuid, expires_in: 6.hours, purpose: :attachment)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ en: &en
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'You have been invited to %{account_name} %{product_name}. Please sign up using the link below:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'You have been invited to %{account_name} %{product_name}. Please sign up using the link below:'
|
||||||
sent_using_product_name_in_testing_mode_html: 'Sent using <a href="%{product_url}">%{product_name}</a> in testing mode'
|
sent_using_product_name_in_testing_mode_html: 'Sent using <a href="%{product_url}">%{product_name}</a> in testing mode'
|
||||||
sent_using_product_name_free_document_signing_html: 'Sent using <a href="%{product_url}">%{product_name}</a> free document signing.'
|
sent_using_product_name_free_document_signing_html: 'Sent using <a href="%{product_url}">%{product_name}</a> free document signing.'
|
||||||
|
sent_using_product_name_open_source_software_html: 'Sent using <a href="%{product_url}">%{product_name}</a> open-source software.'
|
||||||
sent_with_docuseal_pro_html: 'Sent with <a href="%{product_url}">DocuSeal Pro</a>'
|
sent_with_docuseal_pro_html: 'Sent with <a href="%{product_url}">DocuSeal Pro</a>'
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: Show "Sent with <span class="link">DocuSeal Pro</span>" attribution in emails
|
show_send_with_docuseal_pro_attribution_in_emails_html: Show "Sent with <span class="link">DocuSeal Pro</span>" attribution in emails
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Sign documents with trusted certificate provided by DocuSeal. Your documents and data are never shared with DocuSeal. PDF checksum is provided to generate a trusted signature.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Sign documents with trusted certificate provided by DocuSeal. Your documents and data are never shared with DocuSeal. PDF checksum is provided to generate a trusted signature.
|
||||||
@@ -1142,6 +1143,7 @@ es: &es
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Has sido invitado a %{account_name} %{product_name}. Por favor, regístrate usando el enlace a continuación:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Has sido invitado a %{account_name} %{product_name}. Por favor, regístrate usando el enlace a continuación:'
|
||||||
sent_using_product_name_in_testing_mode_html: 'Enviado usando <a href="%{product_url}">%{product_name}</a> en Modo de Prueba'
|
sent_using_product_name_in_testing_mode_html: 'Enviado usando <a href="%{product_url}">%{product_name}</a> en Modo de Prueba'
|
||||||
sent_using_product_name_free_document_signing_html: 'Enviado usando la firma de documentos gratuita de <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Enviado usando la firma de documentos gratuita de <a href="%{product_url}">%{product_name}</a>.'
|
||||||
|
sent_using_product_name_open_source_software_html: 'Enviado usando el software de código abierto de <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sent_with_docuseal_pro_html: 'Enviado con <a href="%{product_url}">DocuSeal Pro</a>'
|
sent_with_docuseal_pro_html: 'Enviado con <a href="%{product_url}">DocuSeal Pro</a>'
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: Mostrar el mensaje "Enviado con <span class="link">DocuSeal Pro</span>" en los correos electrónicos
|
show_send_with_docuseal_pro_attribution_in_emails_html: Mostrar el mensaje "Enviado con <span class="link">DocuSeal Pro</span>" en los correos electrónicos
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Firme documentos con un certificado de confianza proporcionado por DocuSeal. Sus documentos y datos nunca se comparten con DocuSeal. Se proporciona un checksum de PDF para generar una firma de confianza.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Firme documentos con un certificado de confianza proporcionado por DocuSeal. Sus documentos y datos nunca se comparten con DocuSeal. Se proporciona un checksum de PDF para generar una firma de confianza.
|
||||||
@@ -2195,6 +2197,7 @@ it: &it
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sei stato invitato a %{account_name} %{product_name}. Registrati utilizzando il link qui sotto:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sei stato invitato a %{account_name} %{product_name}. Registrati utilizzando il link qui sotto:'
|
||||||
sent_using_product_name_in_testing_mode_html: 'Inviato utilizzando <a href="%{product_url}">%{product_name}</a> in Modalità di Test'
|
sent_using_product_name_in_testing_mode_html: 'Inviato utilizzando <a href="%{product_url}">%{product_name}</a> in Modalità di Test'
|
||||||
sent_using_product_name_free_document_signing_html: 'Inviato utilizzando la firma di documenti gratuita di <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Inviato utilizzando la firma di documenti gratuita di <a href="%{product_url}">%{product_name}</a>.'
|
||||||
|
sent_using_product_name_open_source_software_html: 'Inviato utilizzando il software open source di <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sent_with_docuseal_pro_html: 'Inviato con <a href="%{product_url}">DocuSeal Pro</a>'
|
sent_with_docuseal_pro_html: 'Inviato con <a href="%{product_url}">DocuSeal Pro</a>'
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: Mostra la dicitura "Inviato con <span class="link">DocuSeal Pro</span>" nelle email
|
show_send_with_docuseal_pro_attribution_in_emails_html: Mostra la dicitura "Inviato con <span class="link">DocuSeal Pro</span>" nelle email
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: "Firma documenti con un certificato di fiducia fornito da DocuSeal. I tuoi documenti e i tuoi dati non vengono mai condivisi con DocuSeal. Il checksum PDF è fornito per generare una firma di fiducia."
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: "Firma documenti con un certificato di fiducia fornito da DocuSeal. I tuoi documenti e i tuoi dati non vengono mai condivisi con DocuSeal. Il checksum PDF è fornito per generare una firma di fiducia."
|
||||||
@@ -3248,6 +3251,7 @@ fr: &fr
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Vous avez été invité à %{account_name} %{product_name}. Veuillez vous inscrire en utilisant le lien ci-dessous :'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Vous avez été invité à %{account_name} %{product_name}. Veuillez vous inscrire en utilisant le lien ci-dessous :'
|
||||||
sent_using_product_name_in_testing_mode_html: Envoyé avec <a href="%{product_url}">%{product_name}</a> en mode test
|
sent_using_product_name_in_testing_mode_html: Envoyé avec <a href="%{product_url}">%{product_name}</a> en mode test
|
||||||
sent_using_product_name_free_document_signing_html: Envoyé avec <a href="%{product_url}">%{product_name}</a> signature de documents gratuite.
|
sent_using_product_name_free_document_signing_html: Envoyé avec <a href="%{product_url}">%{product_name}</a> signature de documents gratuite.
|
||||||
|
sent_using_product_name_open_source_software_html: Envoyé avec le logiciel open source <a href="%{product_url}">%{product_name}</a>.
|
||||||
sent_with_docuseal_pro_html: Envoyé avec <a href="%{product_url}">DocuSeal Pro</a>
|
sent_with_docuseal_pro_html: Envoyé avec <a href="%{product_url}">DocuSeal Pro</a>
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: Afficher l’attribution "Envoyé avec <span class="link">DocuSeal Pro</span>" dans les e‑mails
|
show_send_with_docuseal_pro_attribution_in_emails_html: Afficher l’attribution "Envoyé avec <span class="link">DocuSeal Pro</span>" dans les e‑mails
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Signez des documents avec un certificat de confiance fourni par DocuSeal. Vos documents et données ne sont jamais partagés avec DocuSeal. Une empreinte (checksum) PDF est fournie pour générer une signature de confiance.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Signez des documents avec un certificat de confiance fourni par DocuSeal. Vos documents et données ne sont jamais partagés avec DocuSeal. Une empreinte (checksum) PDF est fournie pour générer une signature de confiance.
|
||||||
@@ -4298,6 +4302,7 @@ pt: &pt
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Você foi convidado para %{account_name} %{product_name}. Inscreva-se usando o link abaixo:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Você foi convidado para %{account_name} %{product_name}. Inscreva-se usando o link abaixo:'
|
||||||
sent_using_product_name_in_testing_mode_html: 'Enviado usando <a href="%{product_url}">%{product_name}</a> no Modo de Teste'
|
sent_using_product_name_in_testing_mode_html: 'Enviado usando <a href="%{product_url}">%{product_name}</a> no Modo de Teste'
|
||||||
sent_using_product_name_free_document_signing_html: 'Enviado usando a assinatura gratuita de documentos de <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Enviado usando a assinatura gratuita de documentos de <a href="%{product_url}">%{product_name}</a>.'
|
||||||
|
sent_using_product_name_open_source_software_html: 'Enviado usando o software de código aberto <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sent_with_docuseal_pro_html: 'Enviado com <a href="%{product_url}">DocuSeal Pro</a>'
|
sent_with_docuseal_pro_html: 'Enviado com <a href="%{product_url}">DocuSeal Pro</a>'
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: Mostrar "Enviado com <span class="link">DocuSeal Pro</span>" nos e-mails
|
show_send_with_docuseal_pro_attribution_in_emails_html: Mostrar "Enviado com <span class="link">DocuSeal Pro</span>" nos e-mails
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Assine documentos com certificado confiável fornecido pela DocuSeal. Seus documentos e dados nunca são compartilhados com a DocuSeal. O checksum do PDF é fornecido para gerar uma assinatura confiável.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Assine documentos com certificado confiável fornecido pela DocuSeal. Seus documentos e dados nunca são compartilhados com a DocuSeal. O checksum do PDF é fornecido para gerar uma assinatura confiável.
|
||||||
@@ -5351,6 +5356,7 @@ de: &de
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sie wurden zu %{account_name} %{product_name} eingeladen. Bitte registrieren Sie sich über den folgenden Link:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sie wurden zu %{account_name} %{product_name} eingeladen. Bitte registrieren Sie sich über den folgenden Link:'
|
||||||
sent_using_product_name_in_testing_mode_html: 'Gesendet über <a href="%{product_url}">%{product_name}</a> im Testmodus'
|
sent_using_product_name_in_testing_mode_html: 'Gesendet über <a href="%{product_url}">%{product_name}</a> im Testmodus'
|
||||||
sent_using_product_name_free_document_signing_html: 'Gesendet mit der kostenlosen Dokumentensignierung von <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Gesendet mit der kostenlosen Dokumentensignierung von <a href="%{product_url}">%{product_name}</a>.'
|
||||||
|
sent_using_product_name_open_source_software_html: 'Gesendet mit der Open-Source-Software von <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sent_with_docuseal_pro_html: Gesendet mit <a href="%{product_url}">DocuSeal Pro</a>
|
sent_with_docuseal_pro_html: Gesendet mit <a href="%{product_url}">DocuSeal Pro</a>
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: '"Gesendet mit <span class="link">DocuSeal Pro</span>" in E-Mails anzeigen'
|
show_send_with_docuseal_pro_attribution_in_emails_html: '"Gesendet mit <span class="link">DocuSeal Pro</span>" in E-Mails anzeigen'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Unterzeichnen Sie Dokumente mit einem vertrauenswürdigen Zertifikat von DocuSeal. Ihre Dokumente und Daten werden niemals mit DocuSeal geteilt. Eine PDF-Prüfsumme wird bereitgestellt, um eine vertrauenswürdige Signatur zu generieren.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Unterzeichnen Sie Dokumente mit einem vertrauenswürdigen Zertifikat von DocuSeal. Ihre Dokumente und Daten werden niemals mit DocuSeal geteilt. Eine PDF-Prüfsumme wird bereitgestellt, um eine vertrauenswürdige Signatur zu generieren.
|
||||||
@@ -6808,6 +6814,7 @@ nl: &nl
|
|||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'U bent uitgenodigd voor %{account_name} %{product_name}. Meld u aan via de onderstaande link:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'U bent uitgenodigd voor %{account_name} %{product_name}. Meld u aan via de onderstaande link:'
|
||||||
sent_using_product_name_in_testing_mode_html: Verzonden met <a href="%{product_url}">%{product_name}</a> in testmodus
|
sent_using_product_name_in_testing_mode_html: Verzonden met <a href="%{product_url}">%{product_name}</a> in testmodus
|
||||||
sent_using_product_name_free_document_signing_html: Verzonden met <a href="%{product_url}">%{product_name}</a> gratis documentondertekening.
|
sent_using_product_name_free_document_signing_html: Verzonden met <a href="%{product_url}">%{product_name}</a> gratis documentondertekening.
|
||||||
|
sent_using_product_name_open_source_software_html: Verzonden met de open-source software <a href="%{product_url}">%{product_name}</a>.
|
||||||
sent_with_docuseal_pro_html: Verzonden met <a href="%{product_url}">DocuSeal Pro</a>
|
sent_with_docuseal_pro_html: Verzonden met <a href="%{product_url}">DocuSeal Pro</a>
|
||||||
show_send_with_docuseal_pro_attribution_in_emails_html: Toon de vermelding 'Verzonden met <span class="link">DocuSeal Pro</span>' in e-mails
|
show_send_with_docuseal_pro_attribution_in_emails_html: Toon de vermelding 'Verzonden met <span class="link">DocuSeal Pro</span>' in e-mails
|
||||||
? sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature
|
? sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ Rails.application.routes.draw do
|
|||||||
resource :templates_upload, only: %i[show], path: 'new'
|
resource :templates_upload, only: %i[show], path: 'new'
|
||||||
end
|
end
|
||||||
resources :templates_archived, only: %i[index], path: 'templates/archived'
|
resources :templates_archived, only: %i[index], path: 'templates/archived'
|
||||||
|
resources :templates_shared, only: %i[index], path: 'templates/shared'
|
||||||
resources :folders, only: %i[show edit update destroy], controller: 'template_folders'
|
resources :folders, only: %i[show edit update destroy], controller: 'template_folders'
|
||||||
resources :template_sharings_testing, only: %i[create]
|
resources :template_sharings_testing, only: %i[create]
|
||||||
resources :templates, only: %i[index], controller: 'templates_dashboard'
|
resources :templates, only: %i[index], controller: 'templates_dashboard'
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveSubmittersCompletedAtIndex < ActiveRecord::Migration[8.1]
|
||||||
|
def up
|
||||||
|
remove_index :submitters, %i[completed_at account_id], if_exists: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_index :submitters, %i[completed_at account_id], if_not_exists: true
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddSubmittersCompletedAtPartialIndex < ActiveRecord::Migration[8.1]
|
||||||
|
def up
|
||||||
|
add_index :submitters, %i[account_id completed_at], where: 'completed_at IS NOT NULL', if_not_exists: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_index :submitters, %i[account_id completed_at], if_exists: true
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateEmailMessageAssets < ActiveRecord::Migration[8.1]
|
||||||
|
def change
|
||||||
|
create_table :email_message_assets do |t|
|
||||||
|
t.references :account, null: false, foreign_key: true, index: false
|
||||||
|
t.text :data, null: false
|
||||||
|
t.string :sha1, null: false
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :email_message_assets, %i[account_id sha1], unique: true
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddSubmissionCompletedAt < ActiveRecord::Migration[8.1]
|
||||||
|
def change
|
||||||
|
add_column :submissions, :completed_at, :datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user