Merge from docusealco/wip
@@ -387,7 +387,7 @@ GEM
|
|||||||
puma (6.5.0)
|
puma (6.5.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (3.1.12)
|
rack (3.1.14)
|
||||||
rack-proxy (0.7.7)
|
rack-proxy (0.7.7)
|
||||||
rack
|
rack
|
||||||
rack-session (2.0.0)
|
rack-session (2.0.0)
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ module Api
|
|||||||
|
|
||||||
blob = ActiveStorage::Blob.find_by!(uuid: blob_uuid)
|
blob = ActiveStorage::Blob.find_by!(uuid: blob_uuid)
|
||||||
|
|
||||||
authorization_check!(blob) if exp.blank?
|
attachment = blob.attachments.take
|
||||||
|
|
||||||
|
@record = attachment.record
|
||||||
|
|
||||||
|
authorization_check!(attachment) if exp.blank?
|
||||||
|
|
||||||
if request.headers['Range'].present?
|
if request.headers['Range'].present?
|
||||||
send_blob_byte_range_data blob, request.headers['Range']
|
send_blob_byte_range_data blob, request.headers['Range']
|
||||||
@@ -37,9 +41,7 @@ module Api
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def authorization_check!(blob)
|
def authorization_check!(attachment)
|
||||||
attachment = blob.attachments.take
|
|
||||||
|
|
||||||
is_authorized = attachment.name.in?(%w[logo preview_images]) ||
|
is_authorized = attachment.name.in?(%w[logo preview_images]) ||
|
||||||
(current_user && attachment.record.account.id == current_user.account_id) ||
|
(current_user && attachment.record.account.id == current_user.account_id) ||
|
||||||
(current_user && !Docuseal.multitenant? && current_user.role == 'superadmin') ||
|
(current_user && !Docuseal.multitenant? && current_user.role == 'superadmin') ||
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ module Api
|
|||||||
skip_authorization_check
|
skip_authorization_check
|
||||||
|
|
||||||
def create
|
def create
|
||||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
@submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||||
|
|
||||||
if params[:t] == SubmissionEvents.build_tracking_param(submitter, 'click_email')
|
if params[:t] == SubmissionEvents.build_tracking_param(@submitter, 'click_email')
|
||||||
SubmissionEvents.create_with_tracking_data(submitter, 'click_email', request)
|
SubmissionEvents.create_with_tracking_data(@submitter, 'click_email', request)
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: {}
|
render json: {}
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ module Api
|
|||||||
skip_authorization_check
|
skip_authorization_check
|
||||||
|
|
||||||
def create
|
def create
|
||||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
@submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||||
|
|
||||||
submitter.opened_at = Time.current
|
@submitter.opened_at = Time.current
|
||||||
submitter.save
|
@submitter.save
|
||||||
|
|
||||||
SubmissionEvents.create_with_tracking_data(submitter, 'view_form', request)
|
SubmissionEvents.create_with_tracking_data(@submitter, 'view_form', request)
|
||||||
|
|
||||||
WebhookUrls.for_account_id(submitter.account_id, 'form.viewed').each do |webhook_url|
|
WebhookUrls.for_account_id(@submitter.account_id, 'form.viewed').each do |webhook_url|
|
||||||
SendFormViewedWebhookRequestJob.perform_async('submitter_id' => submitter.id,
|
SendFormViewedWebhookRequestJob.perform_async('submitter_id' => @submitter.id,
|
||||||
'webhook_url_id' => webhook_url.id)
|
'webhook_url_id' => webhook_url.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module Api
|
|||||||
load_and_authorize_resource :template
|
load_and_authorize_resource :template
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize!(:manage, @template)
|
authorize!(:create, @template)
|
||||||
|
|
||||||
ActiveRecord::Associations::Preloader.new(
|
ActiveRecord::Associations::Preloader.new(
|
||||||
records: [@template],
|
records: [@template],
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class PreviewDocumentPageController < ActionController::API
|
|||||||
|
|
||||||
return head :not_found unless attachment
|
return head :not_found unless attachment
|
||||||
|
|
||||||
|
@template = attachment.record
|
||||||
|
|
||||||
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"] })
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ class StartFormController < ApplicationController
|
|||||||
raise ActionController::RoutingError, I18n.t('not_found') if @template.preferences['require_phone_2fa'] == true
|
raise ActionController::RoutingError, I18n.t('not_found') if @template.preferences['require_phone_2fa'] == true
|
||||||
|
|
||||||
@submitter = @template.submissions.new(account_id: @template.account_id)
|
@submitter = @template.submissions.new(account_id: @template.account_id)
|
||||||
.submitters.new(uuid: (filter_undefined_submitters(@template).first ||
|
.submitters.new(account_id: @template.account_id,
|
||||||
|
uuid: (filter_undefined_submitters(@template).first ||
|
||||||
@template.submitters.first)['uuid'])
|
@template.submitters.first)['uuid'])
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -96,6 +97,7 @@ class StartFormController < ApplicationController
|
|||||||
submitter.submission ||= Submission.new(template:,
|
submitter.submission ||= Submission.new(template:,
|
||||||
account_id: template.account_id,
|
account_id: template.account_id,
|
||||||
template_submitters: template.submitters,
|
template_submitters: template.submitters,
|
||||||
|
expire_at: Templates.build_default_expire_at(template),
|
||||||
submitters: [submitter],
|
submitters: [submitter],
|
||||||
source: :link)
|
source: :link)
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ class SubmissionsArchivedController < ApplicationController
|
|||||||
@submissions = @submissions.joins(:template)
|
@submissions = @submissions.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(:created_by_user, template: :author)
|
.preload(:template_accesses, :created_by_user, template: :author)
|
||||||
|
|
||||||
@submissions = @submissions.preload(:template_accesses) unless current_user.role.in?(%w[admin superadmin])
|
|
||||||
|
|
||||||
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
|
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
|
||||||
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ 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(:created_by_user, template: :author)
|
.preload(:template_accesses, :created_by_user, template: :author)
|
||||||
|
|
||||||
@submissions = @submissions.preload(:template_accesses) unless current_user.role.in?(%w[admin superadmin])
|
|
||||||
|
|
||||||
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
|
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
|
||||||
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ class SubmissionsDownloadController < ApplicationController
|
|||||||
FILES_TTL = 5.minutes
|
FILES_TTL = 5.minutes
|
||||||
|
|
||||||
def index
|
def index
|
||||||
submitter = Submitter.find_signed(params[:sig], purpose: :download_completed) if params[:sig].present?
|
@submitter = Submitter.find_signed(params[:sig], purpose: :download_completed) if params[:sig].present?
|
||||||
|
|
||||||
signature_valid =
|
signature_valid =
|
||||||
if submitter&.slug == params[:submitter_slug]
|
if @submitter&.slug == params[:submitter_slug]
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
submitter = nil
|
@submitter = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
submitter ||= Submitter.find_by!(slug: params[:submitter_slug])
|
@submitter ||= Submitter.find_by!(slug: params[:submitter_slug])
|
||||||
|
|
||||||
Submissions::EnsureResultGenerated.call(submitter)
|
Submissions::EnsureResultGenerated.call(@submitter)
|
||||||
|
|
||||||
last_submitter = submitter.submission.submitters.where.not(completed_at: nil).order(:completed_at).last
|
last_submitter = @submitter.submission.submitters.where.not(completed_at: nil).order(:completed_at).last
|
||||||
|
|
||||||
return head :not_found unless last_submitter
|
return head :not_found unless last_submitter
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ class SubmissionsDownloadController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
if params[:combined] == 'true'
|
if params[:combined] == 'true'
|
||||||
url = build_combined_url(submitter)
|
url = build_combined_url(@submitter)
|
||||||
|
|
||||||
if url
|
if url
|
||||||
render json: [url]
|
render json: [url]
|
||||||
|
|||||||
@@ -7,25 +7,25 @@ class SubmitFormDownloadController < ApplicationController
|
|||||||
FILES_TTL = 5.minutes
|
FILES_TTL = 5.minutes
|
||||||
|
|
||||||
def index
|
def index
|
||||||
submitter = Submitter.find_by!(slug: params[:submit_form_slug])
|
@submitter = Submitter.find_by!(slug: params[:submit_form_slug])
|
||||||
|
|
||||||
return redirect_to submitter_download_index_path(submitter.slug) if submitter.completed_at?
|
return redirect_to submitter_download_index_path(@submitter.slug) if @submitter.completed_at?
|
||||||
|
|
||||||
return head :unprocessable_entity if submitter.declined_at? ||
|
return head :unprocessable_entity if @submitter.declined_at? ||
|
||||||
submitter.submission.archived_at? ||
|
@submitter.submission.archived_at? ||
|
||||||
submitter.submission.expired? ||
|
@submitter.submission.expired? ||
|
||||||
submitter.submission.template.archived_at?
|
@submitter.submission.template.archived_at?
|
||||||
|
|
||||||
last_completed_submitter = submitter.submission.submitters
|
last_completed_submitter = @submitter.submission.submitters
|
||||||
.where.not(id: submitter.id)
|
.where.not(id: @submitter.id)
|
||||||
.where.not(completed_at: nil)
|
.where.not(completed_at: nil)
|
||||||
.max_by(&:completed_at)
|
.max_by(&:completed_at)
|
||||||
|
|
||||||
attachments =
|
attachments =
|
||||||
if last_completed_submitter
|
if last_completed_submitter
|
||||||
Submitters.select_attachments_for_download(last_completed_submitter)
|
Submitters.select_attachments_for_download(last_completed_submitter)
|
||||||
else
|
else
|
||||||
submitter.submission.template.schema_documents.preload(:blob)
|
@submitter.submission.template.schema_documents.preload(:blob)
|
||||||
end
|
end
|
||||||
|
|
||||||
urls = attachments.map do |attachment|
|
urls = attachments.map do |attachment|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class TemplatesCloneAndReplaceController < ApplicationController
|
||||||
|
load_and_authorize_resource :template
|
||||||
|
|
||||||
|
def create
|
||||||
|
return head :unprocessable_entity if params[:files].blank?
|
||||||
|
|
||||||
|
ActiveRecord::Associations::Preloader.new(
|
||||||
|
records: [@template],
|
||||||
|
associations: [schema_documents: :preview_images_attachments]
|
||||||
|
).call
|
||||||
|
|
||||||
|
cloned_template = Templates::Clone.call(@template, author: current_user)
|
||||||
|
cloned_template.name = File.basename(params[:files].first.original_filename, '.*')
|
||||||
|
cloned_template.save!
|
||||||
|
|
||||||
|
documents = Templates::ReplaceAttachments.call(cloned_template, params, extract_fields: true)
|
||||||
|
|
||||||
|
cloned_template.save!
|
||||||
|
|
||||||
|
Templates::CloneAttachments.call(template: cloned_template, original_template: @template,
|
||||||
|
excluded_attachment_uuids: documents.map(&:uuid))
|
||||||
|
|
||||||
|
respond_to do |f|
|
||||||
|
f.html { redirect_to edit_template_path(cloned_template) }
|
||||||
|
f.json { render json: { id: cloned_template.id } }
|
||||||
|
end
|
||||||
|
rescue Templates::CreateAttachments::PdfEncrypted
|
||||||
|
respond_to do |f|
|
||||||
|
f.html { render turbo_stream: turbo_stream.append(params[:form_id], html: helpers.tag.prompt_password) }
|
||||||
|
f.json { render json: { error: 'PDF encrypted', status: 'pdf_encrypted' }, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -21,9 +21,7 @@ class TemplatesController < ApplicationController
|
|||||||
submissions.order(id: :desc)
|
submissions.order(id: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
submissions = submissions.preload(:template_accesses) unless current_user.role.in?(%w[admin superadmin])
|
@pagy, @submissions = pagy(submissions.preload(:template_accesses, submitters: :start_form_submission_events))
|
||||||
|
|
||||||
@pagy, @submissions = pagy(submissions.preload(submitters: :start_form_submission_events))
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -45,13 +45,15 @@ class TemplatesDashboardController < ApplicationController
|
|||||||
rel = templates.active.preload(:author, :template_accesses)
|
rel = templates.active.preload(:author, :template_accesses)
|
||||||
|
|
||||||
if params[:q].blank?
|
if params[:q].blank?
|
||||||
if Docuseal.multitenant? && !current_account.testing?
|
if Docuseal.multitenant? ? current_account.testing? : current_account.linked_account_account
|
||||||
rel = rel.where(folder_id: current_account.default_template_folder.id)
|
shared_account_ids = [current_user.account_id]
|
||||||
else
|
shared_account_ids << TemplateSharing::ALL_ID if !Docuseal.multitenant? && !current_account.testing?
|
||||||
shared_template_ids =
|
|
||||||
TemplateSharing.where(account_id: [current_account.id, TemplateSharing::ALL_ID]).select(:template_id)
|
shared_template_ids = TemplateSharing.where(account_id: shared_account_ids).select(:template_id)
|
||||||
|
|
||||||
rel = rel.where(folder_id: current_account.default_template_folder.id).or(rel.where(id: shared_template_ids))
|
rel = rel.where(folder_id: current_account.default_template_folder.id).or(rel.where(id: shared_template_ids))
|
||||||
|
else
|
||||||
|
rel = rel.where(folder_id: current_account.default_template_folder.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,20 @@ class TemplatesPreferencesController < ApplicationController
|
|||||||
completed_notification_email_attach_documents
|
completed_notification_email_attach_documents
|
||||||
completed_redirect_url validate_unique_submitters
|
completed_redirect_url validate_unique_submitters
|
||||||
submitters_order require_phone_2fa
|
submitters_order require_phone_2fa
|
||||||
|
default_expire_at_duration
|
||||||
|
default_expire_at
|
||||||
completed_notification_email_subject completed_notification_email_body
|
completed_notification_email_subject completed_notification_email_body
|
||||||
completed_notification_email_enabled completed_notification_email_attach_audit] +
|
completed_notification_email_enabled completed_notification_email_attach_audit] +
|
||||||
[completed_message: %i[title body],
|
[completed_message: %i[title body],
|
||||||
submitters: [%i[uuid request_email_subject request_email_body]]]
|
submitters: [%i[uuid request_email_subject request_email_body]]]
|
||||||
).tap do |attrs|
|
).tap do |attrs|
|
||||||
attrs[:preferences].delete(:submitters) if params[:request_email_per_submitter] != '1'
|
attrs[:preferences].delete(:submitters) if params[:request_email_per_submitter] != '1'
|
||||||
|
|
||||||
|
if (default_expire_at = attrs.dig(:preferences, :default_expire_at).presence)
|
||||||
|
attrs[:preferences][:default_expire_at] =
|
||||||
|
(ActiveSupport::TimeZone[current_account.timezone] || Time.zone).parse(default_expire_at).utc
|
||||||
|
end
|
||||||
|
|
||||||
attrs[:preferences] = attrs[:preferences].transform_values do |value|
|
attrs[:preferences] = attrs[:preferences].transform_values do |value|
|
||||||
if %w[true false].include?(value)
|
if %w[true false].include?(value)
|
||||||
value == 'true'
|
value == 'true'
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import MaskedInput from './elements/masked_input'
|
|||||||
import SetDateButton from './elements/set_date_button'
|
import SetDateButton from './elements/set_date_button'
|
||||||
import IndeterminateCheckbox from './elements/indeterminate_checkbox'
|
import IndeterminateCheckbox from './elements/indeterminate_checkbox'
|
||||||
import AppTour from './elements/app_tour'
|
import AppTour from './elements/app_tour'
|
||||||
|
import DashboardDropzone from './elements/dashboard_dropzone'
|
||||||
|
|
||||||
import * as TurboInstantClick from './lib/turbo_instant_click'
|
import * as TurboInstantClick from './lib/turbo_instant_click'
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ safeRegisterElement('masked-input', MaskedInput)
|
|||||||
safeRegisterElement('set-date-button', SetDateButton)
|
safeRegisterElement('set-date-button', SetDateButton)
|
||||||
safeRegisterElement('indeterminate-checkbox', IndeterminateCheckbox)
|
safeRegisterElement('indeterminate-checkbox', IndeterminateCheckbox)
|
||||||
safeRegisterElement('app-tour', AppTour)
|
safeRegisterElement('app-tour', AppTour)
|
||||||
|
safeRegisterElement('dashboard-dropzone', DashboardDropzone)
|
||||||
|
|
||||||
safeRegisterElement('template-builder', class extends HTMLElement {
|
safeRegisterElement('template-builder', class extends HTMLElement {
|
||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
@@ -125,6 +127,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
|||||||
withSendButton: this.dataset.withSendButton !== 'false',
|
withSendButton: this.dataset.withSendButton !== 'false',
|
||||||
withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false',
|
withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false',
|
||||||
withConditions: this.dataset.withConditions === 'true',
|
withConditions: this.dataset.withConditions === 'true',
|
||||||
|
withReplaceAndCloneUpload: true,
|
||||||
currencies: (this.dataset.currencies || '').split(',').filter(Boolean),
|
currencies: (this.dataset.currencies || '').split(',').filter(Boolean),
|
||||||
acceptFileTypes: this.dataset.acceptFileTypes,
|
acceptFileTypes: this.dataset.acceptFileTypes,
|
||||||
showTourStartForm: this.dataset.showTourStartForm === 'true'
|
showTourStartForm: this.dataset.showTourStartForm === 'true'
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import { target, targets, targetable } from '@github/catalyst/lib/targetable'
|
||||||
|
|
||||||
|
const loadingIconHtml = `<svg xmlns="http://www.w3.org/2000/svg" class="animate-spin" 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 d="M12 3a9 9 0 1 0 9 9" />
|
||||||
|
</svg>`
|
||||||
|
|
||||||
|
export default targetable(class extends HTMLElement {
|
||||||
|
static [targets.static] = [
|
||||||
|
'hiddenOnDrag',
|
||||||
|
'folderCards',
|
||||||
|
'templateCards'
|
||||||
|
]
|
||||||
|
|
||||||
|
static [target.static] = [
|
||||||
|
'form',
|
||||||
|
'fileDropzone',
|
||||||
|
'fileDropzoneLoading'
|
||||||
|
]
|
||||||
|
|
||||||
|
connectedCallback () {
|
||||||
|
document.addEventListener('drop', this.onWindowDragdrop)
|
||||||
|
document.addEventListener('dragover', this.onWindowDropover)
|
||||||
|
|
||||||
|
window.addEventListener('dragleave', this.onWindowDragleave)
|
||||||
|
|
||||||
|
this.fileDropzone?.addEventListener('drop', this.onDropFile)
|
||||||
|
|
||||||
|
this.folderCards.forEach((el) => el.addEventListener('drop', (e) => this.onDropFolder(e, el)))
|
||||||
|
this.templateCards.forEach((el) => el.addEventListener('drop', this.onDropTemplate))
|
||||||
|
this.templateCards.forEach((el) => el.addEventListener('dragstart', this.onTemplateDragStart))
|
||||||
|
|
||||||
|
return [this.fileDropzone, ...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el?.addEventListener('dragover', this.onDragover)
|
||||||
|
el?.addEventListener('dragleave', this.onDragleave)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback () {
|
||||||
|
document.removeEventListener('drop', this.onWindowDragdrop)
|
||||||
|
document.removeEventListener('dragover', this.onWindowDropover)
|
||||||
|
|
||||||
|
window.removeEventListener('dragleave', this.onWindowDragleave)
|
||||||
|
}
|
||||||
|
|
||||||
|
onTemplateDragStart = (e) => {
|
||||||
|
const id = e.target.href.split('/').pop()
|
||||||
|
|
||||||
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
e.dataTransfer.setData('template_id', id)
|
||||||
|
|
||||||
|
const dragPreview = e.target.cloneNode(true)
|
||||||
|
const rect = e.target.getBoundingClientRect()
|
||||||
|
|
||||||
|
const height = e.target.children[0].getBoundingClientRect().height + 50
|
||||||
|
|
||||||
|
dragPreview.children[1].remove()
|
||||||
|
dragPreview.style.width = `${rect.width}px`
|
||||||
|
dragPreview.style.height = `${height}px`
|
||||||
|
dragPreview.style.position = 'absolute'
|
||||||
|
dragPreview.style.top = '-1000px'
|
||||||
|
dragPreview.style.pointerEvents = 'none'
|
||||||
|
dragPreview.style.opacity = '0.9'
|
||||||
|
|
||||||
|
document.body.appendChild(dragPreview)
|
||||||
|
|
||||||
|
e.dataTransfer.setDragImage(dragPreview, rect.width / 2, height / 2)
|
||||||
|
|
||||||
|
setTimeout(() => document.body.removeChild(dragPreview), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDropFile = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
this.fileDropzoneLoading.classList.remove('hidden')
|
||||||
|
this.fileDropzoneLoading.previousElementSibling.classList.add('hidden')
|
||||||
|
this.fileDropzoneLoading.classList.add('opacity-50')
|
||||||
|
|
||||||
|
this.uploadFiles(e.dataTransfer.files, '/templates_upload')
|
||||||
|
}
|
||||||
|
|
||||||
|
onDropFolder = (e, el) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const templateId = e.dataTransfer.getData('template_id')
|
||||||
|
|
||||||
|
if (e.dataTransfer.files.length || templateId) {
|
||||||
|
const loading = document.createElement('div')
|
||||||
|
const svg = el.querySelector('svg')
|
||||||
|
|
||||||
|
loading.innerHTML = loadingIconHtml
|
||||||
|
loading.children[0].classList.add(...svg.classList)
|
||||||
|
|
||||||
|
el.replaceChild(loading.children[0], svg)
|
||||||
|
el.classList.add('opacity-50')
|
||||||
|
|
||||||
|
if (e.dataTransfer.files.length) {
|
||||||
|
const params = new URLSearchParams({ folder_name: el.innerText }).toString()
|
||||||
|
|
||||||
|
this.uploadFiles(e.dataTransfer.files, `/templates_upload?${params}`)
|
||||||
|
} else {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
formData.append('name', el.innerText)
|
||||||
|
|
||||||
|
fetch(`/templates/${templateId}/folder`, {
|
||||||
|
method: 'PUT',
|
||||||
|
redirect: 'manual',
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
window.Turbo.cache.clear()
|
||||||
|
window.Turbo.visit(location.href)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDropTemplate = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if (e.dataTransfer.files.length) {
|
||||||
|
const loading = document.createElement('div')
|
||||||
|
loading.classList.add('bottom-5', 'left-0', 'flex', 'justify-center', 'w-full', 'absolute')
|
||||||
|
loading.innerHTML = loadingIconHtml
|
||||||
|
|
||||||
|
e.target.appendChild(loading)
|
||||||
|
e.target.classList.add('opacity-50')
|
||||||
|
|
||||||
|
const id = e.target.href.split('/').pop()
|
||||||
|
|
||||||
|
this.uploadFiles(e.dataTransfer.files, `/templates/${id}/clone_and_replace`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowDragdrop = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if (!this.isLoading) this.hideDraghover()
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFiles (files, url) {
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
this.form.action = url
|
||||||
|
|
||||||
|
this.form.querySelector('[type="file"]').files = files
|
||||||
|
|
||||||
|
this.form.querySelector('[type="submit"]').click()
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowDropover = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if (e.dataTransfer?.types?.includes('Files')) {
|
||||||
|
this.showDraghover()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragover (e) {
|
||||||
|
if (e.dataTransfer?.types?.includes('Files') || this.dataset.targets !== 'dashboard-dropzone.templateCards') {
|
||||||
|
this.style.backgroundColor = '#F7F3F0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragleave () {
|
||||||
|
this.style.backgroundColor = null
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowDragleave = (e) => {
|
||||||
|
if (e.clientX <= 0 || e.clientY <= 0 || e.clientX >= window.innerWidth || e.clientY >= window.innerHeight) {
|
||||||
|
this.hideDraghover()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showDraghover = () => {
|
||||||
|
if (this.isDrag) return
|
||||||
|
|
||||||
|
this.isDrag = true
|
||||||
|
|
||||||
|
this.fileDropzone?.classList?.remove('hidden')
|
||||||
|
|
||||||
|
this.hiddenOnDrag.forEach((el) => { el.style.display = 'none' })
|
||||||
|
|
||||||
|
return [...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el.classList.remove('bg-base-200', 'before:hidden')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
hideDraghover = () => {
|
||||||
|
this.isDrag = false
|
||||||
|
|
||||||
|
this.fileDropzone?.classList?.add('hidden')
|
||||||
|
|
||||||
|
this.hiddenOnDrag.forEach((el) => { el.style.display = null })
|
||||||
|
|
||||||
|
return [...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el.classList.add('bg-base-200', 'before:hidden')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -5,7 +5,7 @@ export default actionable(class extends HTMLElement {
|
|||||||
const elementIds = JSON.parse(this.dataset.elementIds)
|
const elementIds = JSON.parse(this.dataset.elementIds)
|
||||||
|
|
||||||
elementIds.forEach((elementId) => {
|
elementIds.forEach((elementId) => {
|
||||||
document.getElementById(elementId).classList.toggle('hidden', event.target.value !== elementId)
|
document.getElementById(elementId).classList.toggle('hidden', (event.target.dataset.toggleId || event.target.value) !== elementId)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,20 @@
|
|||||||
class="mx-auto pl-3 h-full"
|
class="mx-auto pl-3 h-full"
|
||||||
:class="isMobile ? 'pl-4' : 'md:pl-4'"
|
:class="isMobile ? 'pl-4' : 'md:pl-4'"
|
||||||
@dragover="onDragover"
|
@dragover="onDragover"
|
||||||
|
@drop="isDragFile = false"
|
||||||
>
|
>
|
||||||
|
<HoverDropzone
|
||||||
|
v-if="sortedDocuments.length && withUploadButton && editable"
|
||||||
|
:is-dragging="isDragFile"
|
||||||
|
:template-id="template.id"
|
||||||
|
:accept-file-types="acceptFileTypes"
|
||||||
|
:with-replace-and-clone="withReplaceAndCloneUpload"
|
||||||
|
hover-class="bg-base-200/50"
|
||||||
|
@add="[updateFromUpload($event), isDragFile = false]"
|
||||||
|
@replace="[onDocumentsReplace($event), isDragFile = false]"
|
||||||
|
@replace-and-clone="onDocumentsReplaceAndTemplateClone($event)"
|
||||||
|
@error="[onUploadFailed($event), isDragFile = false]"
|
||||||
|
/>
|
||||||
<DragPlaceholder
|
<DragPlaceholder
|
||||||
ref="dragPlaceholder"
|
ref="dragPlaceholder"
|
||||||
:field="fieldsDragFieldRef.value || toRaw(dragField)"
|
:field="fieldsDragFieldRef.value || toRaw(dragField)"
|
||||||
@@ -433,6 +446,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Upload from './upload'
|
import Upload from './upload'
|
||||||
import Dropzone from './dropzone'
|
import Dropzone from './dropzone'
|
||||||
|
import HoverDropzone from './hover_dropzone'
|
||||||
import DragPlaceholder from './drag_placeholder'
|
import DragPlaceholder from './drag_placeholder'
|
||||||
import Fields from './fields'
|
import Fields from './fields'
|
||||||
import MobileDrawField from './mobile_draw_field'
|
import MobileDrawField from './mobile_draw_field'
|
||||||
@@ -462,6 +476,7 @@ export default {
|
|||||||
MobileFields,
|
MobileFields,
|
||||||
Logo,
|
Logo,
|
||||||
Dropzone,
|
Dropzone,
|
||||||
|
HoverDropzone,
|
||||||
DocumentPreview,
|
DocumentPreview,
|
||||||
DocumentControls,
|
DocumentControls,
|
||||||
IconInnerShadowTop,
|
IconInnerShadowTop,
|
||||||
@@ -665,6 +680,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
withReplaceAndCloneUpload: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
withPhone: {
|
withPhone: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -724,7 +744,8 @@ export default {
|
|||||||
copiedArea: null,
|
copiedArea: null,
|
||||||
drawFieldType: null,
|
drawFieldType: null,
|
||||||
drawOption: null,
|
drawOption: null,
|
||||||
dragField: null
|
dragField: null,
|
||||||
|
isDragFile: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -836,6 +857,7 @@ export default {
|
|||||||
window.addEventListener('keydown', this.onKeyDown)
|
window.addEventListener('keydown', this.onKeyDown)
|
||||||
|
|
||||||
window.addEventListener('resize', this.onWindowResize)
|
window.addEventListener('resize', this.onWindowResize)
|
||||||
|
window.addEventListener('dragleave', this.onWindowDragLeave)
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (document.location.search?.includes('stripe_connect_success')) {
|
if (document.location.search?.includes('stripe_connect_success')) {
|
||||||
@@ -854,6 +876,7 @@ export default {
|
|||||||
window.removeEventListener('keydown', this.onKeyDown)
|
window.removeEventListener('keydown', this.onKeyDown)
|
||||||
|
|
||||||
window.removeEventListener('resize', this.onWindowResize)
|
window.removeEventListener('resize', this.onWindowResize)
|
||||||
|
window.removeEventListener('dragleave', this.onWindowDragLeave)
|
||||||
},
|
},
|
||||||
beforeUpdate () {
|
beforeUpdate () {
|
||||||
this.documentRefs = []
|
this.documentRefs = []
|
||||||
@@ -868,6 +891,13 @@ export default {
|
|||||||
|
|
||||||
ref.x = e.clientX - ref.offsetX
|
ref.x = e.clientX - ref.offsetX
|
||||||
ref.y = e.clientY - ref.offsetY
|
ref.y = e.clientY - ref.offsetY
|
||||||
|
} else if (e.dataTransfer?.types?.includes('Files')) {
|
||||||
|
this.isDragFile = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWindowDragLeave (event) {
|
||||||
|
if (event.clientX <= 0 || event.clientY <= 0 || event.clientX >= window.innerWidth || event.clientY >= window.innerHeight) {
|
||||||
|
this.isDragFile = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reorderFields (item) {
|
reorderFields (item) {
|
||||||
@@ -1529,6 +1559,9 @@ export default {
|
|||||||
})
|
})
|
||||||
}, 'image/png')
|
}, 'image/png')
|
||||||
},
|
},
|
||||||
|
onUploadFailed (error) {
|
||||||
|
if (error) alert(error)
|
||||||
|
},
|
||||||
updateFromUpload (data) {
|
updateFromUpload (data) {
|
||||||
this.template.schema.push(...data.schema)
|
this.template.schema.push(...data.schema)
|
||||||
this.template.documents.push(...data.documents)
|
this.template.documents.push(...data.documents)
|
||||||
@@ -1649,6 +1682,29 @@ export default {
|
|||||||
|
|
||||||
this.save()
|
this.save()
|
||||||
},
|
},
|
||||||
|
onDocumentsReplace (data) {
|
||||||
|
data.schema.forEach((schemaItem, index) => {
|
||||||
|
const existingSchemaItem = this.template.schema[index]
|
||||||
|
|
||||||
|
if (this.template.schema[index]) {
|
||||||
|
this.onDocumentReplace({
|
||||||
|
replaceSchemaItem: existingSchemaItem,
|
||||||
|
schema: [schemaItem],
|
||||||
|
documents: [data.documents.find((doc) => doc.uuid === schemaItem.attachment_uuid)]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.updateFromUpload({
|
||||||
|
schema: [schemaItem],
|
||||||
|
documents: [data.documents.find((doc) => doc.uuid === schemaItem.attachment_uuid)],
|
||||||
|
fields: data.fields,
|
||||||
|
submitters: data.submitters
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onDocumentsReplaceAndTemplateClone (template) {
|
||||||
|
window.Turbo.visit(`/templates/${template.id}/edit`)
|
||||||
|
},
|
||||||
moveDocument (item, direction) {
|
moveDocument (item, direction) {
|
||||||
const currentIndex = this.template.schema.indexOf(item)
|
const currentIndex = this.template.schema.indexOf(item)
|
||||||
|
|
||||||
|
|||||||
@@ -2,34 +2,42 @@
|
|||||||
<div
|
<div
|
||||||
class="flex h-60 w-full"
|
class="flex h-60 w-full"
|
||||||
@dragover.prevent
|
@dragover.prevent
|
||||||
|
@dragenter="isDragEntering = true"
|
||||||
|
@dragleave="isDragEntering = false"
|
||||||
@drop.prevent="onDropFiles"
|
@drop.prevent="onDropFiles"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
id="document_dropzone"
|
id="document_dropzone"
|
||||||
class="w-full relative hover:bg-base-200/30 rounded-md border border-2 border-base-content/10 border-dashed"
|
class="w-full relative rounded-md border-2 border-base-content/10 border-dashed"
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
:class="{ 'opacity-50': isLoading || isProcessing }"
|
:class="[{ 'opacity-50': isLoading, 'hover:bg-base-200/30': !hoverClass }, isDragEntering && hoverClass ? hoverClass : '']"
|
||||||
>
|
>
|
||||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
|
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center pointer-events-none">
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
v-if="isLoading || isProcessing"
|
v-if="isLoading"
|
||||||
class="animate-spin"
|
class="animate-spin"
|
||||||
:width="40"
|
:width="40"
|
||||||
:height="40"
|
:height="40"
|
||||||
/>
|
/>
|
||||||
<IconCloudUpload
|
<component
|
||||||
|
:is="icon"
|
||||||
v-else
|
v-else
|
||||||
|
class="stroke-[1.5px]"
|
||||||
:width="40"
|
:width="40"
|
||||||
:height="40"
|
:height="40"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="message"
|
v-if="message"
|
||||||
class="font-medium text-lg mb-1"
|
class="text-lg mb-1"
|
||||||
|
:class="{ 'mt-1': !withDescription, 'font-medium': withDescription }"
|
||||||
>
|
>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm">
|
<div
|
||||||
|
v-if="withDescription"
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
<span class="font-medium">{{ t('click_to_upload') }}</span> {{ t('or_drag_and_drop_files') }}
|
<span class="font-medium">{{ t('click_to_upload') }}</span> {{ t('or_drag_and_drop_files') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,13 +62,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Upload from './upload'
|
import Upload from './upload'
|
||||||
import { IconCloudUpload, IconInnerShadowTop } from '@tabler/icons-vue'
|
import { IconCloudUpload, IconFilePlus, IconFileSymlink, IconFiles, IconInnerShadowTop } from '@tabler/icons-vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FileDropzone',
|
name: 'FileDropzone',
|
||||||
components: {
|
components: {
|
||||||
|
IconFilePlus,
|
||||||
IconCloudUpload,
|
IconCloudUpload,
|
||||||
IconInnerShadowTop
|
IconInnerShadowTop,
|
||||||
|
IconFileSymlink,
|
||||||
|
IconFiles
|
||||||
},
|
},
|
||||||
inject: ['baseFetch', 't'],
|
inject: ['baseFetch', 't'],
|
||||||
props: {
|
props: {
|
||||||
@@ -68,35 +79,70 @@ export default {
|
|||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'IconCloudUpload'
|
||||||
|
},
|
||||||
|
hoverClass: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
cloneTemplateOnUpload: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
withDescription: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
acceptFileTypes: {
|
acceptFileTypes: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: 'image/*, application/pdf'
|
default: 'image/*, application/pdf'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['success'],
|
emits: ['success', 'error', 'loading'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isProcessing: false
|
isDragEntering: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
inputId () {
|
inputId () {
|
||||||
return 'el' + Math.random().toString(32).split('.')[1]
|
return 'el' + Math.random().toString(32).split('.')[1]
|
||||||
},
|
},
|
||||||
|
uploadUrl () {
|
||||||
|
if (this.cloneTemplateOnUpload) {
|
||||||
|
return `/templates/${this.templateId}/clone_and_replace`
|
||||||
|
} else {
|
||||||
|
return `/templates/${this.templateId}/documents`
|
||||||
|
}
|
||||||
|
},
|
||||||
message () {
|
message () {
|
||||||
if (this.isLoading) {
|
if (this.isLoading) {
|
||||||
return this.t('uploading')
|
return this.t('uploading')
|
||||||
} else if (this.isProcessing) {
|
|
||||||
return this.t('processing_')
|
|
||||||
} else if (this.acceptFileTypes === 'image/*, application/pdf') {
|
} else if (this.acceptFileTypes === 'image/*, application/pdf') {
|
||||||
return this.t('add_pdf_documents_or_images')
|
return this.title || this.t('add_pdf_documents_or_images')
|
||||||
} else {
|
} else {
|
||||||
return this.t('add_documents_or_images')
|
return this.title || this.t('add_documents_or_images')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
isLoading (value) {
|
||||||
|
this.$emit('loading', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
upload: Upload.methods.upload,
|
upload: Upload.methods.upload,
|
||||||
onDropFiles (e) {
|
onDropFiles (e) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
v-for="(icon, type) in fieldIconsSorted"
|
v-for="(icon, type) in fieldIconsSorted"
|
||||||
:key="type"
|
:key="type"
|
||||||
>
|
>
|
||||||
<li v-if="(fieldTypes.length === 0 || fieldTypes.includes(type)) && (withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification')">
|
<li v-if="fieldTypes.includes(type) || ((withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification'))">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="text-sm py-1 px-2"
|
class="text-sm py-1 px-2"
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
:key="type"
|
:key="type"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-if="(fieldTypes.length === 0 || fieldTypes.includes(type)) && (withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification')"
|
v-if="fieldTypes.includes(type) || ((withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification'))"
|
||||||
:id="`${type}_type_field_button`"
|
:id="`${type}_type_field_button`"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
class="field-type-button group flex items-center justify-center border border-dashed w-full rounded relative fields-grid-item"
|
class="field-type-button group flex items-center justify-center border border-dashed w-full rounded relative fields-grid-item"
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
:data-tip="t('obtain_qualified_electronic_signature_with_the_trusted_provider_click_to_learn_more')"
|
:data-tip="t('obtain_qualified_electronic_signature_with_the_trusted_provider_click_to_learn_more')"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="https://www.docuseal.com/contact"
|
href="https://www.docuseal.com/qualified-electronic-signature"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="opacity-50 flex items-center justify-center border border-dashed border-base-300 w-full rounded relative fields-grid-item"
|
class="opacity-50 flex items-center justify-center border border-dashed border-base-300 w-full rounded relative fields-grid-item"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="isDragging || isLoading"
|
||||||
|
class="modal modal-open"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-2 p-4 items-center bg-base-100 h-full max-h-[85vh] max-w-6xl rounded-2xl w-full">
|
||||||
|
<Dropzone
|
||||||
|
class="flex-1 h-full"
|
||||||
|
hover-class="bg-base-200/50"
|
||||||
|
icon="IconFilePlus"
|
||||||
|
:template-id="templateId"
|
||||||
|
:accept-file-types="acceptFileTypes"
|
||||||
|
:with-description="false"
|
||||||
|
:title="t('upload_a_new_document')"
|
||||||
|
type="add_files"
|
||||||
|
@loading="isLoading = $event"
|
||||||
|
@success="$emit('add', $event)"
|
||||||
|
@error="$emit('error', $event)"
|
||||||
|
/>
|
||||||
|
<div class="flex-1 flex gap-2 w-full">
|
||||||
|
<Dropzone
|
||||||
|
class="flex-1 h-full"
|
||||||
|
hover-class="bg-base-200/50"
|
||||||
|
icon="IconFileSymlink"
|
||||||
|
:template-id="templateId"
|
||||||
|
:accept-file-types="acceptFileTypes"
|
||||||
|
:with-description="false"
|
||||||
|
:title="t('replace_existing_document')"
|
||||||
|
@loading="isLoading = $event"
|
||||||
|
@success="$emit('replace', $event)"
|
||||||
|
@error="$emit('error', $event)"
|
||||||
|
/>
|
||||||
|
<Dropzone
|
||||||
|
v-if="withReplaceAndClone"
|
||||||
|
class="flex-1 h-full"
|
||||||
|
hover-class="bg-base-200/50"
|
||||||
|
icon="IconFiles"
|
||||||
|
:template-id="templateId"
|
||||||
|
:accept-file-types="acceptFileTypes"
|
||||||
|
:with-description="false"
|
||||||
|
:clone-template-on-upload="true"
|
||||||
|
:title="t('clone_and_replace_documents')"
|
||||||
|
@loading="isLoading = $event"
|
||||||
|
@success="$emit('replace-and-clone', $event)"
|
||||||
|
@error="$emit('error', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Dropzone from './dropzone'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HoverDropzone',
|
||||||
|
components: {
|
||||||
|
Dropzone
|
||||||
|
},
|
||||||
|
inject: ['t'],
|
||||||
|
props: {
|
||||||
|
isDragging: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
templateId: {
|
||||||
|
type: [Number, String],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
withReplaceAndClone: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
acceptFileTypes: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'image/*, application/pdf'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['add', 'replace', 'replace-and-clone', 'error'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isLoading: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -63,6 +63,9 @@ const en = {
|
|||||||
processing_: 'Processing...',
|
processing_: 'Processing...',
|
||||||
add_pdf_documents_or_images: 'Add PDF documents or images',
|
add_pdf_documents_or_images: 'Add PDF documents or images',
|
||||||
add_documents_or_images: 'Add documents or images',
|
add_documents_or_images: 'Add documents or images',
|
||||||
|
upload_a_new_document: 'Upload a new document',
|
||||||
|
replace_existing_document: 'Replace existing document',
|
||||||
|
clone_and_replace_documents: 'Clone and replace documents',
|
||||||
required: 'Required',
|
required: 'Required',
|
||||||
default_value: 'Default value',
|
default_value: 'Default value',
|
||||||
format: 'Format',
|
format: 'Format',
|
||||||
@@ -225,6 +228,9 @@ const es = {
|
|||||||
processing_: 'Procesando...',
|
processing_: 'Procesando...',
|
||||||
add_pdf_documents_or_images: 'Agregar documentos PDF o imágenes',
|
add_pdf_documents_or_images: 'Agregar documentos PDF o imágenes',
|
||||||
add_documents_or_images: 'Agregar documentos o imágenes',
|
add_documents_or_images: 'Agregar documentos o imágenes',
|
||||||
|
upload_a_new_document: 'Subir un nuevo documento',
|
||||||
|
replace_existing_document: 'Reemplazar documento existente',
|
||||||
|
clone_and_replace_documents: 'Clonar y reemplazar documentos',
|
||||||
required: 'Requerido',
|
required: 'Requerido',
|
||||||
default_value: 'Valor predeterminado',
|
default_value: 'Valor predeterminado',
|
||||||
format: 'Formato',
|
format: 'Formato',
|
||||||
@@ -393,6 +399,9 @@ const it = {
|
|||||||
processing_: 'Elaborazione...',
|
processing_: 'Elaborazione...',
|
||||||
add_pdf_documents_or_images: 'Aggiungi documenti PDF o immagini',
|
add_pdf_documents_or_images: 'Aggiungi documenti PDF o immagini',
|
||||||
add_documents_or_images: 'Aggiungi documenti o immagini',
|
add_documents_or_images: 'Aggiungi documenti o immagini',
|
||||||
|
upload_a_new_document: 'Carica un nuovo documento',
|
||||||
|
replace_existing_document: 'Sostituisci documento esistente',
|
||||||
|
clone_and_replace_documents: 'Clona e sostituisci documenti',
|
||||||
required: 'Obbligatorio',
|
required: 'Obbligatorio',
|
||||||
default_value: 'Valore predefinito',
|
default_value: 'Valore predefinito',
|
||||||
format: 'Formato',
|
format: 'Formato',
|
||||||
@@ -555,6 +564,9 @@ const pt = {
|
|||||||
processing_: 'Processando...',
|
processing_: 'Processando...',
|
||||||
add_pdf_documents_or_images: 'Adicionar documentos PDF ou imagens',
|
add_pdf_documents_or_images: 'Adicionar documentos PDF ou imagens',
|
||||||
add_documents_or_images: 'Adicionar documentos ou imagens',
|
add_documents_or_images: 'Adicionar documentos ou imagens',
|
||||||
|
upload_a_new_document: 'Enviar um novo documento',
|
||||||
|
replace_existing_document: 'Substituir documento existente',
|
||||||
|
clone_and_replace_documents: 'Clonar e substituir documentos',
|
||||||
required: 'Obrigatório',
|
required: 'Obrigatório',
|
||||||
default_value: 'Valor padrão',
|
default_value: 'Valor padrão',
|
||||||
format: 'Formato',
|
format: 'Formato',
|
||||||
@@ -719,6 +731,9 @@ const fr = {
|
|||||||
processing_: 'Traitement en cours...',
|
processing_: 'Traitement en cours...',
|
||||||
add_pdf_documents_or_images: 'Ajoutez des documents PDF ou des images',
|
add_pdf_documents_or_images: 'Ajoutez des documents PDF ou des images',
|
||||||
add_documents_or_images: 'Ajoutez des documents ou des images',
|
add_documents_or_images: 'Ajoutez des documents ou des images',
|
||||||
|
upload_a_new_document: 'Téléverser un nouveau document',
|
||||||
|
replace_existing_document: 'Remplacer le document existant',
|
||||||
|
clone_and_replace_documents: 'Cloner et remplacer les documents',
|
||||||
required: 'Requis',
|
required: 'Requis',
|
||||||
default_value: 'Valeur par défaut',
|
default_value: 'Valeur par défaut',
|
||||||
format: 'Format',
|
format: 'Format',
|
||||||
@@ -884,6 +899,9 @@ const de = {
|
|||||||
processing_: 'Verarbeitung...',
|
processing_: 'Verarbeitung...',
|
||||||
add_pdf_documents_or_images: 'PDF-Dokumente oder Bilder hinzufügen',
|
add_pdf_documents_or_images: 'PDF-Dokumente oder Bilder hinzufügen',
|
||||||
add_documents_or_images: 'Dokumente oder Bilder hinzufügen',
|
add_documents_or_images: 'Dokumente oder Bilder hinzufügen',
|
||||||
|
upload_a_new_document: 'Neues Dokument hochladen',
|
||||||
|
replace_existing_document: 'Vorhandenes Dokument ersetzen',
|
||||||
|
clone_and_replace_documents: 'Dokumente klonen und ersetzen',
|
||||||
required: 'Erforderlich',
|
required: 'Erforderlich',
|
||||||
default_value: 'Standardwert',
|
default_value: 'Standardwert',
|
||||||
format: 'Format',
|
format: 'Format',
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
v-for="(icon, type) in fieldIconsSorted"
|
v-for="(icon, type) in fieldIconsSorted"
|
||||||
:key="type"
|
:key="type"
|
||||||
>
|
>
|
||||||
<li v-if="(fieldTypes.length === 0 || fieldTypes.includes(type)) && (withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification')">
|
<li v-if="fieldTypes.includes(type) || ((withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification'))">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="text-sm py-1 px-2"
|
class="text-sm py-1 px-2"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<label
|
<label
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-neutral btn-xs text-white transition-none replace-document-button"
|
class="btn btn-neutral btn-xs text-white transition-none replace-document-button"
|
||||||
:class="{ 'opacity-100': isLoading || isProcessing }"
|
:class="{ 'opacity-100': isLoading }"
|
||||||
>
|
>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
<form
|
<form
|
||||||
@@ -41,19 +41,19 @@ export default {
|
|||||||
emits: ['success'],
|
emits: ['success'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false
|
||||||
isProcessing: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
inputId () {
|
inputId () {
|
||||||
return 'el' + Math.random().toString(32).split('.')[1]
|
return 'el' + Math.random().toString(32).split('.')[1]
|
||||||
},
|
},
|
||||||
|
uploadUrl () {
|
||||||
|
return `/templates/${this.templateId}/documents`
|
||||||
|
},
|
||||||
message () {
|
message () {
|
||||||
if (this.isLoading) {
|
if (this.isLoading) {
|
||||||
return this.t('uploading_')
|
return this.t('uploading_')
|
||||||
} else if (this.isProcessing) {
|
|
||||||
return this.t('processing_')
|
|
||||||
} else {
|
} else {
|
||||||
return this.t('replace')
|
return this.t('replace')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
id="add_document_button"
|
id="add_document_button"
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-outline w-full add-document-button"
|
class="btn btn-outline w-full add-document-button"
|
||||||
:class="{ 'btn-disabled': isLoading || isProcessing }"
|
:class="{ 'btn-disabled': isLoading }"
|
||||||
>
|
>
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
v-if="isLoading || isProcessing"
|
v-if="isLoading"
|
||||||
width="20"
|
width="20"
|
||||||
class="animate-spin"
|
class="animate-spin"
|
||||||
/>
|
/>
|
||||||
@@ -18,9 +18,6 @@
|
|||||||
<span v-if="isLoading">
|
<span v-if="isLoading">
|
||||||
{{ t('uploading_') }}
|
{{ t('uploading_') }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="isProcessing">
|
|
||||||
{{ t('processing_') }}
|
|
||||||
</span>
|
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ t('add_document') }}
|
{{ t('add_document') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -63,30 +60,34 @@ export default {
|
|||||||
default: 'image/*, application/pdf'
|
default: 'image/*, application/pdf'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['success'],
|
emits: ['success', 'error'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false
|
||||||
isProcessing: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
inputId () {
|
inputId () {
|
||||||
return 'el' + Math.random().toString(32).split('.')[1]
|
return 'el' + Math.random().toString(32).split('.')[1]
|
||||||
|
},
|
||||||
|
uploadUrl () {
|
||||||
|
return `/templates/${this.templateId}/documents`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async upload () {
|
async upload () {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
|
|
||||||
this.baseFetch(`/templates/${this.templateId}/documents`, {
|
this.baseFetch(this.uploadUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: { Accept: 'application/json' },
|
||||||
body: new FormData(this.$refs.form)
|
body: new FormData(this.$refs.form)
|
||||||
}).then((resp) => {
|
}).then((resp) => {
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
resp.json().then((data) => {
|
resp.json().then((data) => {
|
||||||
this.$emit('success', data)
|
this.$emit('success', data)
|
||||||
this.$refs.input.value = ''
|
this.$refs.input.value = ''
|
||||||
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
} else if (resp.status === 422) {
|
} else if (resp.status === 422) {
|
||||||
resp.json().then((data) => {
|
resp.json().then((data) => {
|
||||||
@@ -95,21 +96,33 @@ export default {
|
|||||||
|
|
||||||
formData.append('password', prompt(this.t('enter_pdf_password')))
|
formData.append('password', prompt(this.t('enter_pdf_password')))
|
||||||
|
|
||||||
this.baseFetch(`/templates/${this.templateId}/documents`, {
|
this.baseFetch(this.uploadUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
}).then(async (resp) => {
|
}).then(async (resp) => {
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
this.$emit('success', await resp.json())
|
this.$emit('success', await resp.json())
|
||||||
this.$refs.input.value = ''
|
this.$refs.input.value = ''
|
||||||
|
this.isLoading = false
|
||||||
} else {
|
} else {
|
||||||
alert(this.t('wrong_password'))
|
alert(this.t('wrong_password'))
|
||||||
|
|
||||||
|
this.$emit('error', await resp.json().error)
|
||||||
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
this.$emit('error', data.error)
|
||||||
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
resp.json().then((data) => {
|
||||||
|
this.$emit('error', data.error)
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).catch(() => {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class SubmissionEvent < ApplicationRecord
|
|||||||
|
|
||||||
enum :event_type, {
|
enum :event_type, {
|
||||||
send_email: 'send_email',
|
send_email: 'send_email',
|
||||||
|
bounce_email: 'bounce_email',
|
||||||
|
complaint_email: 'complaint_email',
|
||||||
send_reminder_email: 'send_reminder_email',
|
send_reminder_email: 'send_reminder_email',
|
||||||
send_sms: 'send_sms',
|
send_sms: 'send_sms',
|
||||||
send_2fa_sms: 'send_2fa_sms',
|
send_2fa_sms: 'send_2fa_sms',
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class Submitter < ApplicationRecord
|
|||||||
has_many_attached :documents
|
has_many_attached :documents
|
||||||
has_many_attached :attachments
|
has_many_attached :attachments
|
||||||
has_many_attached :preview_documents
|
has_many_attached :preview_documents
|
||||||
has_many :template_accesses, through: :template
|
has_many :template_accesses, through: :submission
|
||||||
has_many :email_events, as: :emailable, dependent: (Docuseal.multitenant? ? nil : :destroy)
|
has_many :email_events, as: :emailable, dependent: (Docuseal.multitenant? ? nil : :destroy)
|
||||||
|
|
||||||
has_many :document_generation_events, dependent: :destroy
|
has_many :document_generation_events, dependent: :destroy
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
<%= t('preferences') %>
|
<%= t('preferences') %>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<% if can?(:manage, account_config) %>
|
<% if can?(:manage, account_config) && (can?(:manage, :personalization_advanced) || !Docuseal.multitenant?) %>
|
||||||
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
||||||
<%= f.hidden_field :key %>
|
<%= f.hidden_field :key %>
|
||||||
<div class="flex items-center justify-between py-2.5">
|
<div class="flex items-center justify-between py-2.5">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
<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 stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M3 9l4 -4l4 4m-4 -4v14" />
|
<path d="M3 9l4 -4l4 4m-4 -4v14" />
|
||||||
<path d="M21 15l-4 4l-4 -4m4 4v-14" />
|
<path d="M21 15l-4 4l-4 -4m4 4v-14" />
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 362 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<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 d="M20.986 12.502a9 9 0 1 0 -5.973 7.98" /><path d="M12 7v5l3 3" /><path d="M19 16v3" /><path d="M19 22v.01" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<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 d="M14 3v4a1 1 0 0 0 1 1h4" /><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z" /><path d="M12 11v6" /><path d="M9.5 13.5l2.5 -2.5l2.5 2.5" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 462 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 d="M15 3v4a1 1 0 0 0 1 1h4" />
|
||||||
|
<path d="M18 17h-7a2 2 0 0 1 -2 -2v-10a2 2 0 0 1 2 -2h4l5 5v7a2 2 0 0 1 -2 2z" />
|
||||||
|
<path d="M16 17v2a2 2 0 0 1 -2 2h-7a2 2 0 0 1 -2 -2v-10a2 2 0 0 1 2 -2h2" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 485 B |
@@ -1,9 +1,3 @@
|
|||||||
<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">
|
<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 stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M4 8v-2a2 2 0 0 1 2 -2h2" /><path d="M4 16v2a2 2 0 0 0 2 2h2" /><path d="M16 4h2a2 2 0 0 1 2 2v2" /><path d="M16 20h2a2 2 0 0 0 2 -2v-2" /><path d="M8 11m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v3a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z" /><path d="M10 11v-2a2 2 0 1 1 4 0v2" />
|
||||||
<path d="M4 8v-2a2 2 0 0 1 2 -2h2"></path>
|
|
||||||
<path d="M4 16v2a2 2 0 0 0 2 2h2"></path>
|
|
||||||
<path d="M16 4h2a2 2 0 0 1 2 2v2"></path>
|
|
||||||
<path d="M16 20h2a2 2 0 0 0 2 -2v-2"></path>
|
|
||||||
<path d="M8 11m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v3a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z"></path>
|
|
||||||
<path d="M10 11v-2a2 2 0 1 1 4 0v2"></path>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 619 B After Width: | Height: | Size: 559 B |
@@ -7,19 +7,19 @@
|
|||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<%= f.fields_for :value, record do |ff| %>
|
<%= f.fields_for :value, record do |ff| %>
|
||||||
<%= ff.label :first_duration, t('first_reminder_in'), class: 'label truncate' %>
|
<%= ff.label :first_duration, t('first_reminder_in'), class: 'label truncate' %>
|
||||||
<%= ff.select :first_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
|
<%= ff.select :first_duration, durations, { include_blank: t('none') }, class: 'base-select' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<%= f.fields_for :value, record do |ff| %>
|
<%= f.fields_for :value, record do |ff| %>
|
||||||
<%= ff.label :second_duration, t('second_reminder_in'), class: 'label truncate' %>
|
<%= ff.label :second_duration, t('second_reminder_in'), class: 'label truncate' %>
|
||||||
<%= ff.select :second_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
|
<%= ff.select :second_duration, durations, { include_blank: t('none') }, class: 'base-select' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<%= f.fields_for :value, record do |ff| %>
|
<%= f.fields_for :value, record do |ff| %>
|
||||||
<%= ff.label :third_duration, t('third_reminder_in'), class: 'label truncate' %>
|
<%= ff.label :third_duration, t('third_reminder_in'), class: 'label truncate' %>
|
||||||
<%= ff.select :third_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
|
<%= ff.select :third_duration, durations, { include_blank: t('none') }, class: 'base-select' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
<%= t('submission_event_names.invite_party_by_html', invited_submitter_name: [invited_submitter.name || invited_submitter.email || invited_submitter.phone, name].join(' '), submitter_name:) %>
|
<%= t('submission_event_names.invite_party_by_html', invited_submitter_name: [invited_submitter.name || invited_submitter.email || invited_submitter.phone, name].join(' '), submitter_name:) %>
|
||||||
<% elsif event.event_type.include?('send_') %>
|
<% elsif event.event_type.include?('send_') %>
|
||||||
<%= t("submission_event_names.#{event.event_type}_to_html", submitter_name:) %>
|
<%= t("submission_event_names.#{event.event_type}_to_html", submitter_name:) %>
|
||||||
|
<% elsif event.event_type.start_with?('bounce_') || event.event_type.start_with?('complaint_') %>
|
||||||
|
<%= t("submission_event_names.#{event.event_type}_html", submitter_name:) %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= t("submission_event_names.#{event.event_type}_by_html", submitter_name:) %>
|
<%= t("submission_event_names.#{event.event_type}_by_html", submitter_name:) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -172,12 +172,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="flex items-center space-x-1 mt-1">
|
<div class="flex items-center space-x-1 mt-1">
|
||||||
<%= svg_icon('writing', class: 'w-5 h-5') %>
|
<% if @submission.expire_at? && submitter && !submitter.completed_at? %>
|
||||||
|
<%= svg_icon('clock_exclamation', class: 'w-5 h-5') %>
|
||||||
|
<% else %>
|
||||||
|
<%= svg_icon('writing', class: 'w-5 h-5') %>
|
||||||
|
<% end %>
|
||||||
<span>
|
<span>
|
||||||
<% if submitter&.declined_at? %>
|
<% if submitter&.declined_at? %>
|
||||||
<%= t('declined_on_time', time: l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
|
<%= t('declined_on_time', time: l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
|
||||||
<% elsif submitter %>
|
<% elsif submitter %>
|
||||||
<%= submitter.completed_at? ? l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) : t('not_completed_yet') %>
|
<% if submitter.completed_at? %>
|
||||||
|
<%= l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) %>
|
||||||
|
<% elsif @submission.expire_at? %>
|
||||||
|
<% if @submission.expired? %>
|
||||||
|
<%= t(:expired) %>
|
||||||
|
<% else %>
|
||||||
|
<%= t('expire_on_time', time: l(@submission.expire_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<%= t('not_completed_yet') %>
|
||||||
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= t('not_invited_yet') %>
|
<%= t('not_invited_yet') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<% is_long = folder.name.size > 32 %>
|
<% is_long = folder.name.size > 32 %>
|
||||||
<a href="<%= folder_path(folder) %>" class="flex h-full flex-col justify-between rounded-2xl py-5 px-6 w-full bg-base-200">
|
<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">
|
||||||
<% if !is_long %>
|
<% if !is_long %>
|
||||||
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -4,47 +4,56 @@
|
|||||||
<span><%= t('home') %></span>
|
<span><%= t('home') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center w-full mb-4">
|
<dashboard-dropzone>
|
||||||
<h1 class="text-4xl font-bold flex flex-grow min-w-0 space-x-2 md:flex <%= 'hidden' if params[:q].present? %>">
|
<div class="relative flex justify-between items-center w-full mb-4">
|
||||||
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
<%= form_for '', url: '', id: form_id = SecureRandom.uuid, method: :post, class: 'hidden', data: { target: 'dashboard-dropzone.form' }, html: { enctype: 'multipart/form-data' } do %>
|
||||||
<span class="peer truncate">
|
<input name="form_id" value="<%= form_id %>">
|
||||||
<%= @template_folder.name %>
|
<input name="folder_name" value="<%= @template_folder.name %>">
|
||||||
</span>
|
<button type="submit"></button>
|
||||||
<% if can?(:update, @template_folder) && @template_folder.name != TemplateFolder::DEFAULT_NAME %>
|
<input id="dashboard_dropzone_input" name="files[]" type="file" multiple>
|
||||||
<span class="pl-1 opacity-0 hover:opacity-100 peer-hover:opacity-100">
|
<% end %>
|
||||||
<a href="<%= edit_folder_path(@template_folder) %>" data-turbo-frame="modal">
|
<%= render 'templates/dashboard_dropzone', style: 'height: 137px' %>
|
||||||
<%= svg_icon('pencil', class: 'w-7 h-7') %>
|
<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? %>">
|
||||||
</a>
|
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
||||||
|
<span class="peer truncate">
|
||||||
|
<%= @template_folder.name %>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% if can?(:update, @template_folder) && @template_folder.name != TemplateFolder::DEFAULT_NAME %>
|
||||||
</h1>
|
<span class="opacity-0 hover:opacity-100 peer-hover:opacity-100">
|
||||||
<div class="flex space-x-2">
|
<a href="<%= edit_folder_path(@template_folder) %>" data-turbo-frame="modal">
|
||||||
<% if params[:q].present? || @pagy.pages > 1 %>
|
<%= svg_icon('pencil', class: 'w-7 h-7') %>
|
||||||
<%= render 'shared/search_input' %>
|
</a>
|
||||||
<% end %>
|
</span>
|
||||||
<% if can?(:create, ::Template) %>
|
<% end %>
|
||||||
<%= render 'templates/upload_button', folder_name: @template_folder.name %>
|
</h1>
|
||||||
<%= link_to new_template_path(folder_name: @template_folder.name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
<div class="flex space-x-2">
|
||||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
<% if params[:q].present? || @pagy.pages > 1 %>
|
||||||
<span class="hidden md:block"><%= t('create') %></span>
|
<%= render 'shared/search_input' %>
|
||||||
|
<% end %>
|
||||||
|
<% if can?(:create, ::Template) %>
|
||||||
|
<%= render 'templates/upload_button', folder_name: @template_folder.name %>
|
||||||
|
<%= link_to new_template_path(folder_name: @template_folder.name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
||||||
|
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||||
|
<span class="hidden md:block"><%= t('create') %></span>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% if @pagy.count > 0 %>
|
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
|
||||||
<%= render partial: 'templates/template', collection: @templates %>
|
|
||||||
</div>
|
|
||||||
<% templates_order_select_html = capture do %>
|
|
||||||
<% if params[:q].blank? && @pagy.pages > 1 %>
|
|
||||||
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates', right_additional_html: templates_order_select_html %>
|
|
||||||
<% elsif params[:q].present? %>
|
|
||||||
<div class="text-center">
|
|
||||||
<div class="mt-16 text-3xl font-semibold">
|
|
||||||
<%= t('templates_not_found') %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% if @pagy.count > 0 %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
|
</div>
|
||||||
|
<% templates_order_select_html = capture do %>
|
||||||
|
<% if params[:q].blank? && @pagy.pages > 1 %>
|
||||||
|
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates', right_additional_html: templates_order_select_html %>
|
||||||
|
<% elsif params[:q].present? %>
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="mt-16 text-3xl font-semibold">
|
||||||
|
<%= t('templates_not_found') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</dashboard-dropzone>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="absolute bottom-0 w-full cursor-pointer rounded-xl bg-base-100 border-2 border-base-300 border-dashed hidden z-50" data-target="dashboard-dropzone.fileDropzone" style="<%= local_assigns[:style] %>">
|
||||||
|
<div class="absolute top-0 right-0 left-0 bottom-0 flex justify-center p-2 items-center pointer-events-none">
|
||||||
|
<div class="flex flex-col items-center text-center" data-target="dashboard-dropzone.toggleLoading">
|
||||||
|
<span class="flex flex-col items-center">
|
||||||
|
<span>
|
||||||
|
<%= svg_icon('cloud_upload', class: 'w-9 h-9') %>
|
||||||
|
</span>
|
||||||
|
<div class="font-medium mb-1">
|
||||||
|
<%= t('upload_new_document') %>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class="flex flex-col items-center hidden" data-target="dashboard-dropzone.fileDropzoneLoading">
|
||||||
|
<%= svg_icon('loader', class: 'w-9 h-9 animate-spin') %>
|
||||||
|
<div class="font-medium mb-1">
|
||||||
|
<%= t('uploading') %>...
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<div class="h-36 relative group">
|
<div class="h-36 relative group">
|
||||||
<a href="<%= template_path(template) %>" class="flex h-full flex-col justify-between rounded-2xl pt-6 px-7 w-full bg-base-200 peer">
|
<a href="<%= template_path(template) %>" class="flex h-full flex-col justify-between rounded-2xl pt-6 px-7 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" data-targets="dashboard-dropzone.templateCards">
|
||||||
<div class="pb-4 text-xl font-semibold" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
|
<div class="text-xl font-semibold" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
|
||||||
<% if template.template_accesses.present? %>
|
<% if template.template_accesses.present? %>
|
||||||
<%= svg_icon('lock', class: 'w-6 h-6 inline -translate-y-[4px]') %>
|
<%= svg_icon('lock', class: 'w-6 h-6 inline -translate-y-[4px]') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% template.name.split(/(_)/).each do |item| %><%= item %><wbr><% end %>
|
<% template.name.split(/(_)/).each do |item| %><%= item %><wbr><% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-6 pt-1 space-y-1">
|
<div class="pb-6 pt-1 space-y-1" data-targets="dashboard-dropzone.hiddenOnDrag">
|
||||||
<p class="flex items-center space-x-1 text-xs text-base-content/60">
|
<p class="flex items-center space-x-1 text-xs text-base-content/60">
|
||||||
<%= 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>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div class="absolute top-0 bottom-0 w-0 flex items-center hidden md:group-hover:flex" style="right: 37px">
|
<div class="absolute top-0 bottom-0 w-0 items-center hidden md:group-hover:flex" style="right: 37px" data-targets="dashboard-dropzone.hiddenOnDrag">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<% if can?(:update, template) && !template.archived_at? && template.account_id == current_account.id %>
|
<% if can?(:update, template) && !template.archived_at? && template.account_id == current_account.id %>
|
||||||
<span class="tooltip tooltip-left" data-tip="<%= t('move') %>">
|
<span class="tooltip tooltip-left" data-tip="<%= t('move') %>">
|
||||||
|
|||||||
@@ -14,6 +14,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<input type="hidden" name="form_id" value="<%= form_id %>">
|
<input type="hidden" name="form_id" value="<%= form_id %>">
|
||||||
<input id="upload_template" name="files[]" class="hidden" onchange="this.form.requestSubmit()" type="file" accept="image/*, application/pdf<%= ', .docx, .doc, .xlsx, .xls, .odt, .rtf' if Docuseal.multitenant? %>" multiple>
|
<input id="upload_template" name="files[]" class="hidden" onchange="this.form.requestSubmit()" type="file" accept="image/*, application/pdf<%= ', .docx, .doc, .xlsx, .xls, .odt, .rtf' if Docuseal.advanced_formats? %>" multiple>
|
||||||
<input hidden name="folder_name" value="<%= local_assigns[:folder_name] %>">
|
<input hidden name="folder_name" value="<%= local_assigns[:folder_name] %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -61,7 +61,9 @@
|
|||||||
<div class="flex items-end flex-col md:flex-row gap-2 w-full md:w-fit">
|
<div class="flex items-end flex-col md:flex-row gap-2 w-full md:w-fit">
|
||||||
<%= render 'submissions_filters/applied_filters', filter_params: %>
|
<%= render 'submissions_filters/applied_filters', filter_params: %>
|
||||||
<%= render 'submissions_filters/filter_button', filter_params: %>
|
<%= render 'submissions_filters/filter_button', filter_params: %>
|
||||||
<%= render 'submissions_filters/actions', filter_params: filter_params.merge(template_id: @template.id) %>
|
<% unless @template.archived_at? %>
|
||||||
|
<%= render 'submissions_filters/actions', filter_params: filter_params.merge(template_id: @template.id) %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,76 +1,86 @@
|
|||||||
<% has_archived = current_account.templates.where.not(archived_at: nil).exists? %>
|
<% has_archived = current_account.templates.where.not(archived_at: nil).exists? %>
|
||||||
<% show_dropzone = params[:q].blank? && @pagy.pages == 1 && ((@template_folders.size < 10 && @templates.size.zero?) || (@template_folders.size < 7 && @templates.size < 4) || (@template_folders.size < 4 && @templates.size < 7)) %>
|
<% show_dropzone = params[:q].blank? && @pagy.pages == 1 && ((@template_folders.size < 10 && @templates.size.zero?) || (@template_folders.size < 7 && @templates.size < 4) || (@template_folders.size < 4 && @templates.size < 7)) %>
|
||||||
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
||||||
<div class="flex justify-between items-center w-full mb-4">
|
<dashboard-dropzone>
|
||||||
<div class="flex items-center flex-grow min-w-0">
|
<%= form_for '', url: '', id: form_id = SecureRandom.uuid, method: :post, class: 'hidden', data: { target: 'dashboard-dropzone.form' }, html: { enctype: 'multipart/form-data' } do %>
|
||||||
<% if has_archived || @pagy.count > 0 || @template_folders.present? %>
|
<input name="form_id" value="<%= form_id %>">
|
||||||
<div class="mr-2">
|
<button type="submit"></button>
|
||||||
<%= render 'dashboard/toggle_view', selected: 'templates' %>
|
<input id="dashboard_dropzone_input" name="files[]" type="file" multiple>
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<h1 class="text-2xl truncate md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
|
|
||||||
<%= t('document_templates_html') %>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div class="flex space-x-2">
|
|
||||||
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
|
||||||
<%= render 'shared/search_input' %>
|
|
||||||
<% end %>
|
|
||||||
<% if can?(:create, ::Template) %>
|
|
||||||
<span class="hidden sm:block">
|
|
||||||
<%= render 'templates/upload_button' %>
|
|
||||||
</span>
|
|
||||||
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
|
||||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
|
||||||
<span class="hidden md:block"><%= t('create') %></span>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% view_archived_html = capture do %>
|
|
||||||
<% if has_archived %>
|
|
||||||
<div>
|
|
||||||
<a href="<%= templates_archived_index_path %>" class="link text-sm"><%= t('view_archived') %></a>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<div class="flex justify-between items-center w-full mb-4 relative">
|
||||||
<% templates_order_select_html = capture do %>
|
<% unless show_dropzone %>
|
||||||
<% if params[:q].blank? && @pagy.pages > 1 %>
|
<%= render 'templates/dashboard_dropzone', style: 'height: 114px' %>
|
||||||
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
<% end %>
|
||||||
<% end %>
|
<div class="flex items-center flex-grow min-w-0">
|
||||||
<% end %>
|
<% if has_archived || @pagy.count > 0 || @template_folders.present? %>
|
||||||
<% if @template_folders.present? %>
|
<div class="mr-2">
|
||||||
<div class="grid gap-4 md:grid-cols-3 <%= 'mb-6' if @templates.present? %>">
|
<%= render 'dashboard/toggle_view', selected: 'templates' %>
|
||||||
<%= render partial: 'template_folders/folder', collection: @template_folders, as: :folder %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% if @templates.present? %>
|
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
|
||||||
<%= render partial: 'templates/template', collection: @templates %>
|
|
||||||
<% if show_dropzone && current_user.created_at > 2.weeks.ago || params[:tour] == 'true' %>
|
|
||||||
<% user_config = current_user.user_configs.find_or_initialize_by(key: UserConfig::SHOW_APP_TOUR) %>
|
|
||||||
<% if user_config.new_record? || user_config.value || params[:tour] == 'true' %>
|
|
||||||
<div class="hidden md:block">
|
|
||||||
<app-tour id="app_tour" data-show-tour="<%= params[:tour] == 'true' || user_config.value %>" data-type="dashboard" data-next-page-path="<%= @templates.first && can?(:edit, @templates.first) ? edit_template_path(@templates.first, params.permit(:tour)) : settings_account_path %>" data-i18n="<%= t('app_tour').to_json %>"></app-tour>
|
|
||||||
<% if user_config.new_record? && !params.key?(:tour) %>
|
|
||||||
<div class="h-36 rounded-2xl pt-3 px-7 w-full border border-dashed border-base-300">
|
|
||||||
<div class="text-xl text-center font-semibold text-base-content">
|
|
||||||
<%= t('welcome_to_docuseal') %>
|
|
||||||
</div>
|
|
||||||
<div class="my-2 text-center text-xs text-base-content/70">
|
|
||||||
<%= t('start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document') %>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 mt-3 w-full">
|
|
||||||
<%= button_to button_title(title: t('skip'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: false } }, class: 'btn btn-sm btn-outline w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.parentNode.remove()' } %>
|
|
||||||
<%= button_to t('start_tour'), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: true } }, class: 'btn btn-sm btn-warning w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.start()' } %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<h1 class="text-2xl truncate md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
|
||||||
|
<%= t('document_templates_html') %>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
||||||
|
<%= render 'shared/search_input' %>
|
||||||
|
<% end %>
|
||||||
|
<% if can?(:create, ::Template) %>
|
||||||
|
<span class="hidden sm:block">
|
||||||
|
<%= render 'templates/upload_button' %>
|
||||||
|
</span>
|
||||||
|
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
||||||
|
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||||
|
<span class="hidden md:block"><%= t('create') %></span>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% view_archived_html = capture do %>
|
||||||
|
<% if has_archived %>
|
||||||
|
<div>
|
||||||
|
<a href="<%= templates_archived_index_path %>" class="link text-sm"><%= t('view_archived') %></a>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% templates_order_select_html = capture do %>
|
||||||
|
<% if params[:q].blank? && @pagy.pages > 1 %>
|
||||||
|
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% if @template_folders.present? %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3 <%= 'mb-6' if @templates.present? %>">
|
||||||
|
<%= render partial: 'template_folders/folder', collection: @template_folders, as: :folder %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if @templates.present? %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
|
<% if show_dropzone && current_user.created_at > 2.weeks.ago || params[:tour] == 'true' %>
|
||||||
|
<% user_config = current_user.user_configs.find_or_initialize_by(key: UserConfig::SHOW_APP_TOUR) %>
|
||||||
|
<% if user_config.new_record? || user_config.value || params[:tour] == 'true' %>
|
||||||
|
<div class="hidden md:block">
|
||||||
|
<app-tour id="app_tour" data-show-tour="<%= params[:tour] == 'true' || user_config.value %>" data-type="dashboard" data-next-page-path="<%= @templates.first && can?(:edit, @templates.first) ? edit_template_path(@templates.first, params.permit(:tour)) : settings_account_path %>" data-i18n="<%= t('app_tour').to_json %>"></app-tour>
|
||||||
|
<% if user_config.new_record? && !params.key?(:tour) %>
|
||||||
|
<div class="h-36 rounded-2xl pt-3 px-7 w-full border border-dashed border-base-300">
|
||||||
|
<div class="text-xl text-center font-semibold text-base-content">
|
||||||
|
<%= t('welcome_to_docuseal') %>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 text-center text-xs text-base-content/70">
|
||||||
|
<%= t('start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document') %>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 mt-3 w-full">
|
||||||
|
<%= button_to button_title(title: t('skip'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: false } }, class: 'btn btn-sm btn-outline w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.parentNode.remove()' } %>
|
||||||
|
<%= button_to t('start_tour'), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: true } }, class: 'btn btn-sm btn-warning w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.start()' } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</dashboard-dropzone>
|
||||||
<% if show_dropzone %>
|
<% if show_dropzone %>
|
||||||
<%= render 'templates/dropzone' %>
|
<%= render 'templates/dropzone' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= tag.input type: 'email', multiple: true, name: 'template[preferences][bcc_completed]', autocomplete: 'off', class: 'base-input', value: ff.object.bcc_completed %>
|
<%= tag.input type: 'email', multiple: true, name: 'template[preferences][bcc_completed]', autocomplete: 'off', class: 'base-input', value: ff.object.bcc_completed, id: ff.field_id(:bcc_completed) %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="form-control pt-3">
|
<div class="form-control pt-3">
|
||||||
@@ -41,7 +41,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'templates_code_modal/preferences' %>
|
<%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mb-5' }, data: { close_on_submit: false } do |f| %>
|
||||||
|
<%= f.fields_for :preferences, Struct.new(:default_expire_at_duration, :default_expire_at).new(@template.preferences['default_expire_at_duration'], @template.preferences['default_expire_at'] ? Time.zone.parse(@template.preferences['default_expire_at']).in_time_zone(current_account.timezone) : nil) do |ff| %>
|
||||||
|
<div class="form-control">
|
||||||
|
<% duration_options = Templates::EXPIRATION_DURATIONS.keys.map { |duration| [t(duration), duration] } + [[t('specified_date'), 'specified_date']] %>
|
||||||
|
<%= ff.label :default_expire_at_duration, t('default_expiration'), class: 'label pt-0' %>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<%= ff.select :default_expire_at_duration, duration_options, { include_blank: t('none') }, required: false, class: 'base-select flex-1', dir: 'auto', autocomplete: 'off', onchange: "this.value == 'specified_date' ? window.template_preferences_default_expire_at.classList.remove('hidden') : [window.template_preferences_default_expire_at.classList.add('hidden'), window.template_preferences_default_expire_at.value = '', this.form.requestSubmit()]" %>
|
||||||
|
<%= ff.datetime_field :default_expire_at, required: false, class: ['base-input flex-1', ff.object.default_expire_at.blank? && 'hidden'].compact_blank.join(' '), dir: 'auto', autocomplete: 'off', onchange: 'this.value && this.form.requestSubmit()' %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
<div class="collapse collapse-arrow join-item border border-base-300 mb-2">
|
<div class="collapse collapse-arrow join-item border border-base-300 mb-2">
|
||||||
<input type="checkbox" name="accordion">
|
<input type="checkbox" name="accordion">
|
||||||
<div class="collapse-title text-xl font-medium">
|
<div class="collapse-title text-xl font-medium">
|
||||||
@@ -273,6 +284,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<%= render 'templates_code_modal/preferences', class: 'pt-2' %>
|
||||||
</div>
|
</div>
|
||||||
<% if show_recipients %>
|
<% if show_recipients %>
|
||||||
<div id="recipients" class="hidden mt-2 mb-4 px-5">
|
<div id="recipients" class="hidden mt-2 mb-4 px-5">
|
||||||
|
|||||||
@@ -134,7 +134,15 @@ Rails.application.configure do
|
|||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resource = controller.instance_variable_get(:@submitter) ||
|
||||||
|
controller.instance_variable_get(:@submission) ||
|
||||||
|
controller.instance_variable_get(:@template) ||
|
||||||
|
controller.instance_variable_get(:@record)
|
||||||
|
|
||||||
|
current_user = controller.instance_variable_get(:@current_user)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
host: controller.request.host,
|
||||||
fwd: controller.request.remote_ip,
|
fwd: controller.request.remote_ip,
|
||||||
params: {
|
params: {
|
||||||
id: params[:id],
|
id: params[:id],
|
||||||
@@ -148,8 +156,10 @@ Rails.application.configure do
|
|||||||
params[:submit_form_slug] ||
|
params[:submit_form_slug] ||
|
||||||
params[:template_slug]).to_s.first(5)
|
params[:template_slug]).to_s.first(5)
|
||||||
}.compact_blank,
|
}.compact_blank,
|
||||||
host: controller.request.host,
|
uid: current_user.try(:id),
|
||||||
uid: controller.instance_variable_get(:@current_user).try(:id)
|
aid: current_user.try(:account_id),
|
||||||
|
rid: resource.try(:id),
|
||||||
|
raid: resource.try(:account_id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ en: &en
|
|||||||
language_ja: 日本語
|
language_ja: 日本語
|
||||||
hi_there: Hi there
|
hi_there: Hi there
|
||||||
thanks: Thanks
|
thanks: Thanks
|
||||||
|
private: Private
|
||||||
bcc_recipients: BCC recipients
|
bcc_recipients: BCC recipients
|
||||||
resend_pending: Re-send pending
|
resend_pending: Re-send pending
|
||||||
always_enforce_signing_order: Always enforce the signing order
|
always_enforce_signing_order: Always enforce the signing order
|
||||||
@@ -597,6 +598,7 @@ en: &en
|
|||||||
not_invited_yet: Not invited yet
|
not_invited_yet: Not invited yet
|
||||||
not_completed_yet: Not completed yet
|
not_completed_yet: Not completed yet
|
||||||
declined_on_time: 'Declined on %{time}'
|
declined_on_time: 'Declined on %{time}'
|
||||||
|
expire_on_time: 'Expire on %{time}'
|
||||||
sign_in_person: Sign In-person
|
sign_in_person: Sign In-person
|
||||||
create_a_new_template_document_form_or_submit_the_existing_one_html: <a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Create a new template</a> document form or <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">submit the existing one</a>
|
create_a_new_template_document_form_or_submit_the_existing_one_html: <a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Create a new template</a> document form or <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">submit the existing one</a>
|
||||||
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Send email copy with completed documents to a specified BCC address.
|
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Send email copy with completed documents to a specified BCC address.
|
||||||
@@ -717,6 +719,25 @@ en: &en
|
|||||||
name_a_z: Name A-Z
|
name_a_z: Name A-Z
|
||||||
recently_used: Recently used
|
recently_used: Recently used
|
||||||
newest_first: Newest first
|
newest_first: Newest first
|
||||||
|
none: None
|
||||||
|
default_expiration: Default expiration
|
||||||
|
specified_date: Specified date
|
||||||
|
one_day: 1 day
|
||||||
|
two_days: 2 days
|
||||||
|
three_days: 3 days
|
||||||
|
four_days: 4 days
|
||||||
|
five_days: 5 days
|
||||||
|
six_days: 6 days
|
||||||
|
seven_days: 7 days
|
||||||
|
eight_days: 8 days
|
||||||
|
nine_days: 9 days
|
||||||
|
ten_days: 10 days
|
||||||
|
two_weeks: 2 weeks
|
||||||
|
three_weeks: 3 weeks
|
||||||
|
four_weeks: 4 weeks
|
||||||
|
one_month: 1 month
|
||||||
|
two_months: 2 months
|
||||||
|
three_months: 3 months
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Bulk Send
|
bulk: Bulk Send
|
||||||
@@ -725,6 +746,8 @@ en: &en
|
|||||||
link: Link
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>Email sent</b> to %{submitter_name}'
|
send_email_to_html: '<b>Email sent</b> to %{submitter_name}'
|
||||||
|
bounce_email_html: '<b>Email bounced</b> %{submitter_name}'
|
||||||
|
complaint_email_html: '<b>Spam complaint</b> %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>Reminder email sent</b> to %{submitter_name}'
|
send_reminder_email_to_html: '<b>Reminder email sent</b> to %{submitter_name}'
|
||||||
send_sms_to_html: '<b>SMS sent</b> to %{submitter_name}'
|
send_sms_to_html: '<b>SMS sent</b> to %{submitter_name}'
|
||||||
send_2fa_sms_to_html: '<b>Verification SMS sent</b> to %{submitter_name}'
|
send_2fa_sms_to_html: '<b>Verification SMS sent</b> to %{submitter_name}'
|
||||||
@@ -760,11 +783,11 @@ en: &en
|
|||||||
previous: Previous
|
previous: Previous
|
||||||
next: Next
|
next: Next
|
||||||
template_and_submissions: 'Templates and Submissions'
|
template_and_submissions: 'Templates and Submissions'
|
||||||
template_and_submissions_description: "You can select the view that best suits your workflow. Choose the 'Templates' view if you create reusable document templates, or the 'Submissions' view when you sign individual documents or review the status of each signing request."
|
template_and_submissions_description: "You can select the view that best suits your workflow. Choose the 'Templates' view to create reusable document templates, or the 'Submissions' view to sign individual documents or review the status of each signature request."
|
||||||
upload_a_pdf_file: 'Upload a PDF file'
|
upload_a_pdf_file: 'Upload a PDF file'
|
||||||
upload_a_pdf_file_description: 'Upload a document to use it for creating a signing form template.'
|
upload_a_pdf_file_description: 'Upload a PDF document to create a signing form template.'
|
||||||
select_a_signer_party: 'Select a signer party'
|
select_a_signer_party: 'Select a signer party'
|
||||||
select_a_signer_party_description: 'This dropdown allows you to select a signing party role or add a new party to the document when it needs to be signed by two or more parties.'
|
select_a_signer_party_description: 'This dropdown allows you to select a signing party role or add a new party to the document if it requires 2 or more signatures.'
|
||||||
available_parties: 'Available parties'
|
available_parties: 'Available parties'
|
||||||
available_parties_description: 'This list contains all available parties with their role names. Once selected, you can add and configure fields for the signer party.'
|
available_parties_description: 'This list contains all available parties with their role names. Once selected, you can add and configure fields for the signer party.'
|
||||||
available_field_types: 'Available field types'
|
available_field_types: 'Available field types'
|
||||||
@@ -772,7 +795,7 @@ en: &en
|
|||||||
text_input_field: 'Text input field'
|
text_input_field: 'Text input field'
|
||||||
text_input_field_description: 'This field allows users to enter text-based information, such as names or emails.'
|
text_input_field_description: 'This field allows users to enter text-based information, such as names or emails.'
|
||||||
signature_field: 'Signature field'
|
signature_field: 'Signature field'
|
||||||
signature_field_description: 'This field allows for collecting signatures from the signers.'
|
signature_field_description: 'This field is used to collect signatures from the signers.'
|
||||||
added_fields: 'Added fields'
|
added_fields: 'Added fields'
|
||||||
added_fields_description: 'This list displays all fields added to the document. You can reorder, edit, or remove them.'
|
added_fields_description: 'This list displays all fields added to the document. You can reorder, edit, or remove them.'
|
||||||
open_field_settings: 'Open field settings'
|
open_field_settings: 'Open field settings'
|
||||||
@@ -786,10 +809,10 @@ en: &en
|
|||||||
copy_and_share_link: 'Copy & share link'
|
copy_and_share_link: 'Copy & share link'
|
||||||
copy_and_share_link_description: 'Copy this link to share the document. Anyone with the link can sign it after entering their email.'
|
copy_and_share_link_description: 'Copy this link to share the document. Anyone with the link can sign it after entering their email.'
|
||||||
sign_the_document: 'Sign the document'
|
sign_the_document: 'Sign the document'
|
||||||
sign_the_document_description: 'If you are one of the signers signer, this button allows you to sign the document yourself.'
|
sign_the_document_description: 'If you are one of the signers, this button allows you to sign the document.'
|
||||||
send_for_signing: 'Send for signing'
|
send_for_signing: 'Send for signing'
|
||||||
add_recipients: 'Add recipients'
|
add_recipients: 'Add recipients'
|
||||||
add_recipients_description: 'Add new recipients for the document to be signed. You can enter their email addresses or phone numbers.'
|
add_recipients_description: 'Add new recipients for the document to be signed by entering their email addresses or phone numbers.'
|
||||||
settings: 'Settings'
|
settings: 'Settings'
|
||||||
settings_account_description: 'With comprehensive account settings, you can customize the eSigning experience and invite more users.'
|
settings_account_description: 'With comprehensive account settings, you can customize the eSigning experience and invite more users.'
|
||||||
support: 'Support'
|
support: 'Support'
|
||||||
@@ -801,6 +824,7 @@ en: &en
|
|||||||
read: Read your data
|
read: Read your data
|
||||||
|
|
||||||
es: &es
|
es: &es
|
||||||
|
private: Privado
|
||||||
resend_pending: Reenviar pendiente
|
resend_pending: Reenviar pendiente
|
||||||
ensure_unique_recipients: Asegurar destinatarios únicos
|
ensure_unique_recipients: Asegurar destinatarios únicos
|
||||||
require_phone_2fa_to_open: Requiere 2FA por teléfono para abrir
|
require_phone_2fa_to_open: Requiere 2FA por teléfono para abrir
|
||||||
@@ -1379,6 +1403,7 @@ es: &es
|
|||||||
not_invited_yet: Aún no invitado
|
not_invited_yet: Aún no invitado
|
||||||
not_completed_yet: Aún no completado
|
not_completed_yet: Aún no completado
|
||||||
declined_on_time: 'Rechazado el %{time}'
|
declined_on_time: 'Rechazado el %{time}'
|
||||||
|
expire_on_time: 'Expira el %{time}'
|
||||||
sign_in_person: Firma en persona
|
sign_in_person: Firma en persona
|
||||||
create_a_new_template_document_form_or_submit_the_existing_one_html: <a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Crear una nueva plantilla</a> de documento o <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">enviar el existente</a>
|
create_a_new_template_document_form_or_submit_the_existing_one_html: <a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Crear una nueva plantilla</a> de documento o <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">enviar el existente</a>
|
||||||
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Enviar una copia del correo electrónico con los documentos completados a una dirección BCC especificada.
|
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Enviar una copia del correo electrónico con los documentos completados a una dirección BCC especificada.
|
||||||
@@ -1499,6 +1524,25 @@ es: &es
|
|||||||
name_a_z: Nombre A-Z
|
name_a_z: Nombre A-Z
|
||||||
recently_used: Usado recientemente
|
recently_used: Usado recientemente
|
||||||
newest_first: Más reciente primero
|
newest_first: Más reciente primero
|
||||||
|
none: Ninguno
|
||||||
|
default_expiration: Vencimiento predeterminado
|
||||||
|
specified_date: Fecha especificada
|
||||||
|
one_day: 1 día
|
||||||
|
two_days: 2 días
|
||||||
|
three_days: 3 días
|
||||||
|
four_days: 4 días
|
||||||
|
five_days: 5 días
|
||||||
|
six_days: 6 días
|
||||||
|
seven_days: 7 días
|
||||||
|
eight_days: 8 días
|
||||||
|
nine_days: 9 días
|
||||||
|
ten_days: 10 días
|
||||||
|
two_weeks: 2 semanas
|
||||||
|
three_weeks: 3 semanas
|
||||||
|
four_weeks: 4 semanas
|
||||||
|
one_month: 1 mes
|
||||||
|
two_months: 2 meses
|
||||||
|
three_months: 3 meses
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Envío masivo
|
bulk: Envío masivo
|
||||||
@@ -1507,6 +1551,8 @@ es: &es
|
|||||||
link: Enlace
|
link: Enlace
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>Correo electrónico enviado</b> a %{submitter_name}'
|
send_email_to_html: '<b>Correo electrónico enviado</b> a %{submitter_name}'
|
||||||
|
bounce_email_html: '<b>Correo electrónico rebotado</b> %{submitter_name}'
|
||||||
|
complaint_email_html: '<b>Queja de spam</b> %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>Correo de recordatorio enviado</b> a %{submitter_name}'
|
send_reminder_email_to_html: '<b>Correo de recordatorio enviado</b> a %{submitter_name}'
|
||||||
send_sms_to_html: '<b>SMS enviado</b> a %{submitter_name}'
|
send_sms_to_html: '<b>SMS enviado</b> a %{submitter_name}'
|
||||||
send_2fa_sms_to_html: '<b>SMS de verificación enviado</b> a %{submitter_name}'
|
send_2fa_sms_to_html: '<b>SMS de verificación enviado</b> a %{submitter_name}'
|
||||||
@@ -1542,11 +1588,11 @@ es: &es
|
|||||||
previous: Anterior
|
previous: Anterior
|
||||||
next: Siguiente
|
next: Siguiente
|
||||||
template_and_submissions: 'Plantillas y Envíos'
|
template_and_submissions: 'Plantillas y Envíos'
|
||||||
template_and_submissions_description: "Puedes seleccionar la vista que mejor se adapte a tu flujo de trabajo. Elige la vista de 'Plantillas' si creas plantillas reutilizables de documentos, o la vista de 'Envíos' cuando firmas documentos individuales o revisas el estado de cada solicitud de firma."
|
template_and_submissions_description: "Puedes seleccionar la vista que mejor se adapte a tu flujo de trabajo. Elige la vista 'Plantillas' para crear plantillas reutilizables de documentos, o la vista 'Envíos' para firmar documentos individuales o revisar el estado de cada solicitud de firma."
|
||||||
upload_a_pdf_file: 'Subir un archivo PDF'
|
upload_a_pdf_file: 'Subir un archivo PDF'
|
||||||
upload_a_pdf_file_description: 'Sube un documento para usarlo como plantilla de formulario de firma.'
|
upload_a_pdf_file_description: 'Sube un documento PDF para crear una plantilla de formulario de firma.'
|
||||||
select_a_signer_party: 'Seleccionar parte firmante'
|
select_a_signer_party: 'Seleccionar parte firmante'
|
||||||
select_a_signer_party_description: 'Este menú desplegable te permite seleccionar un rol de firmante o agregar una nueva parte al documento cuando debe ser firmado por dos o más partes.'
|
select_a_signer_party_description: 'Este menú desplegable te permite seleccionar un rol de firmante o agregar una nueva parte al documento si requiere 2 o más firmas.'
|
||||||
available_parties: 'Partes disponibles'
|
available_parties: 'Partes disponibles'
|
||||||
available_parties_description: 'Esta lista contiene todas las partes disponibles con sus roles. Una vez seleccionada, puedes añadir y configurar campos para esa parte.'
|
available_parties_description: 'Esta lista contiene todas las partes disponibles con sus roles. Una vez seleccionada, puedes añadir y configurar campos para esa parte.'
|
||||||
available_field_types: 'Tipos de campo disponibles'
|
available_field_types: 'Tipos de campo disponibles'
|
||||||
@@ -1554,7 +1600,7 @@ es: &es
|
|||||||
text_input_field: 'Campo de texto'
|
text_input_field: 'Campo de texto'
|
||||||
text_input_field_description: 'Este campo permite a los usuarios introducir información de texto, como nombres o correos electrónicos.'
|
text_input_field_description: 'Este campo permite a los usuarios introducir información de texto, como nombres o correos electrónicos.'
|
||||||
signature_field: 'Campo de firma'
|
signature_field: 'Campo de firma'
|
||||||
signature_field_description: 'Este campo permite recopilar firmas de los firmantes.'
|
signature_field_description: 'Este campo se utiliza para recopilar las firmas de los firmantes.'
|
||||||
added_fields: 'Campos añadidos'
|
added_fields: 'Campos añadidos'
|
||||||
added_fields_description: 'Esta lista muestra todos los campos añadidos al documento. Puedes reordenarlos, editarlos o eliminarlos.'
|
added_fields_description: 'Esta lista muestra todos los campos añadidos al documento. Puedes reordenarlos, editarlos o eliminarlos.'
|
||||||
open_field_settings: 'Abrir configuración del campo'
|
open_field_settings: 'Abrir configuración del campo'
|
||||||
@@ -1568,10 +1614,10 @@ es: &es
|
|||||||
copy_and_share_link: 'Copiar y compartir enlace'
|
copy_and_share_link: 'Copiar y compartir enlace'
|
||||||
copy_and_share_link_description: 'Copia este enlace para compartir el documento. Cualquiera con el enlace puede firmarlo tras ingresar su correo electrónico.'
|
copy_and_share_link_description: 'Copia este enlace para compartir el documento. Cualquiera con el enlace puede firmarlo tras ingresar su correo electrónico.'
|
||||||
sign_the_document: 'Firmar el documento'
|
sign_the_document: 'Firmar el documento'
|
||||||
sign_the_document_description: 'Si eres uno de los firmantes, este botón te permite firmar el documento tú mismo.'
|
sign_the_document_description: 'Si eres uno de los firmantes, este botón te permite firmar el documento.'
|
||||||
send_for_signing: 'Enviar para firma'
|
send_for_signing: 'Enviar para firma'
|
||||||
add_recipients: 'Agregar destinatarios'
|
add_recipients: 'Agregar destinatarios'
|
||||||
add_recipients_description: 'Agrega nuevos destinatarios para que el documento sea firmado. Puedes ingresar sus correos electrónicos o números de teléfono.'
|
add_recipients_description: 'Agrega nuevos destinatarios para que firmen el documento ingresando sus correos electrónicos o números de teléfono.'
|
||||||
settings: 'Configuración'
|
settings: 'Configuración'
|
||||||
settings_account_description: 'Con una configuración completa de la cuenta, puedes personalizar la experiencia de firma y añadir más usuarios.'
|
settings_account_description: 'Con una configuración completa de la cuenta, puedes personalizar la experiencia de firma y añadir más usuarios.'
|
||||||
support: 'Soporte'
|
support: 'Soporte'
|
||||||
@@ -1583,6 +1629,7 @@ es: &es
|
|||||||
read: Leer tus datos
|
read: Leer tus datos
|
||||||
|
|
||||||
it: &it
|
it: &it
|
||||||
|
private: Privato
|
||||||
resend_pending: Reinvia in sospeso
|
resend_pending: Reinvia in sospeso
|
||||||
ensure_unique_recipients: Assicurarsi destinatari unici
|
ensure_unique_recipients: Assicurarsi destinatari unici
|
||||||
require_phone_2fa_to_open: Richiedi l'autenticazione a due fattori tramite telefono per aprire
|
require_phone_2fa_to_open: Richiedi l'autenticazione a due fattori tramite telefono per aprire
|
||||||
@@ -2160,6 +2207,7 @@ it: &it
|
|||||||
not_invited_yet: Non ancora invitato
|
not_invited_yet: Non ancora invitato
|
||||||
not_completed_yet: Non ancora completato
|
not_completed_yet: Non ancora completato
|
||||||
declined_on_time: 'Rifiutato il %{time}'
|
declined_on_time: 'Rifiutato il %{time}'
|
||||||
|
expire_on_time: 'Scade il %{time}'
|
||||||
sign_in_person: Firma di persona
|
sign_in_person: Firma di persona
|
||||||
create_a_new_template_document_form_or_submit_the_existing_one_html: <a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Crea un nuovo modello</a> di documento o <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">invia quello esistente</a>
|
create_a_new_template_document_form_or_submit_the_existing_one_html: <a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Crea un nuovo modello</a> di documento o <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">invia quello esistente</a>
|
||||||
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Invia una copia dell'email con i documenti completati a un indirizzo BCC specificato.
|
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Invia una copia dell'email con i documenti completati a un indirizzo BCC specificato.
|
||||||
@@ -2280,6 +2328,25 @@ it: &it
|
|||||||
name_a_z: Nome A-Z
|
name_a_z: Nome A-Z
|
||||||
recently_used: Recentemente usato
|
recently_used: Recentemente usato
|
||||||
newest_first: Più recenti prima
|
newest_first: Più recenti prima
|
||||||
|
none: Nessuno
|
||||||
|
default_expiration: Scadenza predefinita
|
||||||
|
specified_date: Data specificata
|
||||||
|
one_day: 1 giorno
|
||||||
|
two_days: 2 giorni
|
||||||
|
three_days: 3 giorni
|
||||||
|
four_days: 4 giorni
|
||||||
|
five_days: 5 giorni
|
||||||
|
six_days: 6 giorni
|
||||||
|
seven_days: 7 giorni
|
||||||
|
eight_days: 8 giorni
|
||||||
|
nine_days: 9 giorni
|
||||||
|
ten_days: 10 giorni
|
||||||
|
two_weeks: 2 settimane
|
||||||
|
three_weeks: 3 settimane
|
||||||
|
four_weeks: 4 settimane
|
||||||
|
one_month: 1 mese
|
||||||
|
two_months: 2 mesi
|
||||||
|
three_months: 3 mesi
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Invio massivo
|
bulk: Invio massivo
|
||||||
@@ -2287,7 +2354,9 @@ it: &it
|
|||||||
invite: Invito
|
invite: Invito
|
||||||
link: Link
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-mail inviato</b> a %{submitter_name}'
|
send_email_to_html: '<b>Email inviata</b> a %{submitter_name}'
|
||||||
|
bounce_email_html: '<b>Email respinta</b> %{submitter_name}'
|
||||||
|
complaint_email_html: '<b>Segnalazione spam</b> %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>E-mail di promemoria inviato</b> a %{submitter_name}'
|
send_reminder_email_to_html: '<b>E-mail di promemoria inviato</b> a %{submitter_name}'
|
||||||
send_sms_to_html: '<b>SMS inviato</b> a %{submitter_name}'
|
send_sms_to_html: '<b>SMS inviato</b> a %{submitter_name}'
|
||||||
send_2fa_sms_to_html: '<b>SMS di verifica inviato</b> a %{submitter_name}'
|
send_2fa_sms_to_html: '<b>SMS di verifica inviato</b> a %{submitter_name}'
|
||||||
@@ -2323,11 +2392,11 @@ it: &it
|
|||||||
previous: Precedente
|
previous: Precedente
|
||||||
next: Successivo
|
next: Successivo
|
||||||
template_and_submissions: 'Modelli e Invii'
|
template_and_submissions: 'Modelli e Invii'
|
||||||
template_and_submissions_description: "Puoi selezionare la vista che meglio si adatta al tuo flusso di lavoro. Scegli la vista 'Modelli' se crei modelli riutilizzabili di documenti, oppure la vista 'Invii' quando firmi documenti singoli o controlli lo stato di ogni richiesta di firma."
|
template_and_submissions_description: "Puoi selezionare la visualizzazione più adatta al tuo flusso di lavoro. Scegli la visualizzazione 'Modelli' per creare modelli di documenti riutilizzabili o 'Invii' per firmare documenti singoli o controllare lo stato di ciascuna richiesta di firma."
|
||||||
upload_a_pdf_file: 'Carica un file PDF'
|
upload_a_pdf_file: 'Carica un file PDF'
|
||||||
upload_a_pdf_file_description: 'Carica un documento da utilizzare per creare un modello di modulo di firma.'
|
upload_a_pdf_file_description: 'Carica un documento PDF per creare un modello di modulo di firma.'
|
||||||
select_a_signer_party: 'Seleziona una parte firmataria'
|
select_a_signer_party: 'Seleziona una parte firmataria'
|
||||||
select_a_signer_party_description: 'Questo menu a discesa consente di selezionare un ruolo di firmatario o aggiungere una nuova parte al documento quando deve essere firmato da due o più parti.'
|
select_a_signer_party_description: 'Questo menu a discesa consente di selezionare un ruolo di firmatario o aggiungere una nuova parte al documento se richiede 2 o più firme.'
|
||||||
available_parties: 'Parti disponibili'
|
available_parties: 'Parti disponibili'
|
||||||
available_parties_description: 'Questo elenco contiene tutte le parti disponibili con i relativi ruoli. Una volta selezionata, puoi aggiungere e configurare i campi per la parte firmataria.'
|
available_parties_description: 'Questo elenco contiene tutte le parti disponibili con i relativi ruoli. Una volta selezionata, puoi aggiungere e configurare i campi per la parte firmataria.'
|
||||||
available_field_types: 'Tipi di campo disponibili'
|
available_field_types: 'Tipi di campo disponibili'
|
||||||
@@ -2335,7 +2404,7 @@ it: &it
|
|||||||
text_input_field: 'Campo di testo'
|
text_input_field: 'Campo di testo'
|
||||||
text_input_field_description: 'Questo campo consente agli utenti di inserire informazioni testuali, come nomi o email.'
|
text_input_field_description: 'Questo campo consente agli utenti di inserire informazioni testuali, come nomi o email.'
|
||||||
signature_field: 'Campo firma'
|
signature_field: 'Campo firma'
|
||||||
signature_field_description: 'Questo campo consente di raccogliere firme dai firmatari.'
|
signature_field_description: 'Questo campo viene utilizzato per raccogliere le firme dei firmatari.'
|
||||||
added_fields: 'Campi aggiunti'
|
added_fields: 'Campi aggiunti'
|
||||||
added_fields_description: 'Questo elenco mostra tutti i campi aggiunti al documento. Puoi riordinarli, modificarli o rimuoverli.'
|
added_fields_description: 'Questo elenco mostra tutti i campi aggiunti al documento. Puoi riordinarli, modificarli o rimuoverli.'
|
||||||
open_field_settings: 'Apri le impostazioni del campo'
|
open_field_settings: 'Apri le impostazioni del campo'
|
||||||
@@ -2349,10 +2418,10 @@ it: &it
|
|||||||
copy_and_share_link: 'Copia e condividi il link'
|
copy_and_share_link: 'Copia e condividi il link'
|
||||||
copy_and_share_link_description: 'Copia questo link per condividere il documento. Chiunque abbia il link potrà firmarlo dopo aver inserito la propria email.'
|
copy_and_share_link_description: 'Copia questo link per condividere il documento. Chiunque abbia il link potrà firmarlo dopo aver inserito la propria email.'
|
||||||
sign_the_document: 'Firma il documento'
|
sign_the_document: 'Firma il documento'
|
||||||
sign_the_document_description: 'Se sei uno dei firmatari, questo pulsante ti consente di firmare il documento personalmente.'
|
sign_the_document_description: 'Se sei uno dei firmatari, questo pulsante ti consente di firmare il documento.'
|
||||||
send_for_signing: 'Invia per la firma'
|
send_for_signing: 'Invia per la firma'
|
||||||
add_recipients: 'Aggiungi destinatari'
|
add_recipients: 'Aggiungi destinatari'
|
||||||
add_recipients_description: 'Aggiungi nuovi destinatari per la firma del documento. Puoi inserire il loro indirizzo email o numero di telefono.'
|
add_recipients_description: 'Aggiungi nuovi destinatari inserendo i loro indirizzi email o numeri di telefono per far firmare il documento.'
|
||||||
settings: 'Impostazioni'
|
settings: 'Impostazioni'
|
||||||
settings_account_description: "Con impostazioni complete dell'account, puoi personalizzare l'esperienza di firma elettronica e invitare altri utenti."
|
settings_account_description: "Con impostazioni complete dell'account, puoi personalizzare l'esperienza di firma elettronica e invitare altri utenti."
|
||||||
support: 'Supporto'
|
support: 'Supporto'
|
||||||
@@ -2364,6 +2433,7 @@ it: &it
|
|||||||
read: Leggi i tuoi dati
|
read: Leggi i tuoi dati
|
||||||
|
|
||||||
fr: &fr
|
fr: &fr
|
||||||
|
private: Privé
|
||||||
resend_pending: Renvoyer en attente
|
resend_pending: Renvoyer en attente
|
||||||
ensure_unique_recipients: Assurer l'unicité des destinataires
|
ensure_unique_recipients: Assurer l'unicité des destinataires
|
||||||
require_phone_2fa_to_open: Requiert une 2FA par téléphone pour ouvrir
|
require_phone_2fa_to_open: Requiert une 2FA par téléphone pour ouvrir
|
||||||
@@ -2943,6 +3013,7 @@ fr: &fr
|
|||||||
not_invited_yet: Pas encore invité
|
not_invited_yet: Pas encore invité
|
||||||
not_completed_yet: Pas encore terminé
|
not_completed_yet: Pas encore terminé
|
||||||
declined_on_time: 'Refusé le %{time}'
|
declined_on_time: 'Refusé le %{time}'
|
||||||
|
expire_on_time: 'Expire le %{time}'
|
||||||
sign_in_person: Signature en personne
|
sign_in_person: Signature en personne
|
||||||
create_a_new_template_document_form_or_submit_the_existing_one_html: "<a href=\"%{new_template_link}\" data-turbo-frame=\"modal\" class=\"inline underline font-medium\">Créer un nouveau modèle</a> de document ou <a href=\"%{start_form_link}\" target=\"_blank\" class=\"inline underline font-medium\">soumettre l'existant</a>"
|
create_a_new_template_document_form_or_submit_the_existing_one_html: "<a href=\"%{new_template_link}\" data-turbo-frame=\"modal\" class=\"inline underline font-medium\">Créer un nouveau modèle</a> de document ou <a href=\"%{start_form_link}\" target=\"_blank\" class=\"inline underline font-medium\">soumettre l'existant</a>"
|
||||||
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Envoyer une copie de l'e-mail avec les documents complétés à une adresse BCC spécifiée.
|
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Envoyer une copie de l'e-mail avec les documents complétés à une adresse BCC spécifiée.
|
||||||
@@ -3056,13 +3127,32 @@ fr: &fr
|
|||||||
submission_created_via_source_html: '<b>Soumission créée</b> via %{source}'
|
submission_created_via_source_html: '<b>Soumission créée</b> via %{source}'
|
||||||
pro_user_seats_used: Places utilisateur Pro en cours d'utilisation
|
pro_user_seats_used: Places utilisateur Pro en cours d'utilisation
|
||||||
manage_plan: Gérer le plan
|
manage_plan: Gérer le plan
|
||||||
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: 'Cette soumission comporte plusieurs signataires, ce qui empêche l’utilisation d’un lien de partage, car il n’est pas clair quel signataire est responsable de quels champs. Pour résoudre ce problème, suivez ce <a href="https://www.docuseal.com/resources/pre-filling-recipients" class="link font-bold" rel="noopener noreferrer nofollow" target="_blank">guide</a> pour définir les détails du signataire par défaut.'
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: "Cette soumission comporte plusieurs signataires, ce qui empêche l'utilisation d'un lien de partage, car il n'est pas clair quel signataire est responsable de quels champs. Pour résoudre ce problème, suivez ce <a href=\"https://www.docuseal.com/resources/pre-filling-recipients\" class=\"link font-bold\" rel=\"noopener noreferrer nofollow\" target=\"_blank\">guide</a> pour définir les détails du signataire par défaut."
|
||||||
welcome_to_docuseal: Bienvenue sur DocuSeal
|
welcome_to_docuseal: Bienvenue sur DocuSeal
|
||||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: Lancez une visite rapide pour apprendre à créer et envoyer votre premier document.
|
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: Lancez une visite rapide pour apprendre à créer et envoyer votre premier document.
|
||||||
start_tour: Démarrer
|
start_tour: Démarrer
|
||||||
name_a_z: Nom A-Z
|
name_a_z: Nom A-Z
|
||||||
recently_used: Récemment utilisé
|
recently_used: Récemment utilisé
|
||||||
newest_first: Le plus récent d'abord
|
newest_first: Le plus récent d'abord
|
||||||
|
none: Aucun
|
||||||
|
default_expiration: Expiration par défaut
|
||||||
|
specified_date: Date spécifiée
|
||||||
|
one_day: 1 jour
|
||||||
|
two_days: 2 jours
|
||||||
|
three_days: 3 jours
|
||||||
|
four_days: 4 jours
|
||||||
|
five_days: 5 jours
|
||||||
|
six_days: 6 jours
|
||||||
|
seven_days: 7 jours
|
||||||
|
eight_days: 8 jours
|
||||||
|
nine_days: 9 jours
|
||||||
|
ten_days: 10 jours
|
||||||
|
two_weeks: 2 semaines
|
||||||
|
three_weeks: 3 semaines
|
||||||
|
four_weeks: 4 semaines
|
||||||
|
one_month: 1 mois
|
||||||
|
two_months: 2 mois
|
||||||
|
three_months: 3 mois
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Envoi en masse
|
bulk: Envoi en masse
|
||||||
@@ -3071,6 +3161,8 @@ fr: &fr
|
|||||||
link: Lien
|
link: Lien
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-mail envoyé</b> à %{submitter_name}'
|
send_email_to_html: '<b>E-mail envoyé</b> à %{submitter_name}'
|
||||||
|
bounce_email_html: '<b>Email non distribué</b> %{submitter_name}'
|
||||||
|
complaint_email_html: '<b>Réclamation de spam</b> %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>E-mail de rappel envoyé</b> à %{submitter_name}'
|
send_reminder_email_to_html: '<b>E-mail de rappel envoyé</b> à %{submitter_name}'
|
||||||
send_sms_to_html: '<b>SMS envoyé</b> à %{submitter_name}'
|
send_sms_to_html: '<b>SMS envoyé</b> à %{submitter_name}'
|
||||||
send_2fa_sms_to_html: '<b>SMS de vérification envoyé</b> à %{submitter_name}'
|
send_2fa_sms_to_html: '<b>SMS de vérification envoyé</b> à %{submitter_name}'
|
||||||
@@ -3106,11 +3198,11 @@ fr: &fr
|
|||||||
previous: Précédent
|
previous: Précédent
|
||||||
next: Suivant
|
next: Suivant
|
||||||
template_and_submissions: 'Modèles et Soumissions'
|
template_and_submissions: 'Modèles et Soumissions'
|
||||||
template_and_submissions_description: "Vous pouvez sélectionner la vue qui correspond le mieux à votre flux de travail. Choisissez la vue 'Modèles' si vous créez des modèles de documents réutilisables, ou la vue 'Soumissions' lorsque vous signez des documents individuels ou vérifiez l'état de chaque demande de signature."
|
template_and_submissions_description: "Vous pouvez sélectionner la vue qui correspond le mieux à votre flux de travail. Choisissez la vue 'Modèles' pour créer des modèles de documents réutilisables ou la vue 'Soumissions' pour signer des documents individuels ou vérifier l'état de chaque demande de signature."
|
||||||
upload_a_pdf_file: 'Téléverser un fichier PDF'
|
upload_a_pdf_file: 'Téléverser un fichier PDF'
|
||||||
upload_a_pdf_file_description: "Téléversez un document pour l'utiliser comme modèle de formulaire de signature."
|
upload_a_pdf_file_description: 'Téléversez un document PDF pour créer un modèle de formulaire de signature.'
|
||||||
select_a_signer_party: 'Sélectionner une partie signataire'
|
select_a_signer_party: 'Sélectionner une partie signataire'
|
||||||
select_a_signer_party_description: "Ce menu déroulant vous permet de sélectionner un rôle de signataire ou d'ajouter une nouvelle partie au document lorsqu'il doit être signé par deux parties ou plus."
|
select_a_signer_party_description: "Ce menu déroulant vous permet de sélectionner un rôle de signataire ou d'ajouter une nouvelle partie au document s'il nécessite 2 signatures ou plus."
|
||||||
available_parties: 'Parties disponibles'
|
available_parties: 'Parties disponibles'
|
||||||
available_parties_description: 'Cette liste contient toutes les parties disponibles avec leurs rôles. Une fois sélectionnée, vous pouvez ajouter et configurer les champs pour cette partie.'
|
available_parties_description: 'Cette liste contient toutes les parties disponibles avec leurs rôles. Une fois sélectionnée, vous pouvez ajouter et configurer les champs pour cette partie.'
|
||||||
available_field_types: 'Types de champs disponibles'
|
available_field_types: 'Types de champs disponibles'
|
||||||
@@ -3118,7 +3210,7 @@ fr: &fr
|
|||||||
text_input_field: 'Champ de texte'
|
text_input_field: 'Champ de texte'
|
||||||
text_input_field_description: 'Ce champ permet aux utilisateurs de saisir des informations textuelles telles que des noms ou des adresses e-mail.'
|
text_input_field_description: 'Ce champ permet aux utilisateurs de saisir des informations textuelles telles que des noms ou des adresses e-mail.'
|
||||||
signature_field: 'Champ de signature'
|
signature_field: 'Champ de signature'
|
||||||
signature_field_description: 'Ce champ permet de collecter les signatures des signataires.'
|
signature_field_description: 'Ce champ est utilisé pour recueillir les signatures des signataires.'
|
||||||
added_fields: 'Champs ajoutés'
|
added_fields: 'Champs ajoutés'
|
||||||
added_fields_description: 'Cette liste affiche tous les champs ajoutés au document. Vous pouvez les réorganiser, les modifier ou les supprimer.'
|
added_fields_description: 'Cette liste affiche tous les champs ajoutés au document. Vous pouvez les réorganiser, les modifier ou les supprimer.'
|
||||||
open_field_settings: 'Ouvrir les paramètres du champ'
|
open_field_settings: 'Ouvrir les paramètres du champ'
|
||||||
@@ -3132,10 +3224,10 @@ fr: &fr
|
|||||||
copy_and_share_link: 'Copier et partager le lien'
|
copy_and_share_link: 'Copier et partager le lien'
|
||||||
copy_and_share_link_description: 'Copiez ce lien pour partager le document. Toute personne disposant du lien pourra le signer après avoir saisi son e-mail.'
|
copy_and_share_link_description: 'Copiez ce lien pour partager le document. Toute personne disposant du lien pourra le signer après avoir saisi son e-mail.'
|
||||||
sign_the_document: 'Signer le document'
|
sign_the_document: 'Signer le document'
|
||||||
sign_the_document_description: 'Si vous êtes l’un des signataires, ce bouton vous permet de signer le document vous-même.'
|
sign_the_document_description: "Si vous êtes l'n des signataires, ce bouton vous permet de signer le document."
|
||||||
send_for_signing: 'Envoyer pour signature'
|
send_for_signing: 'Envoyer pour signature'
|
||||||
add_recipients: 'Ajouter des destinataires'
|
add_recipients: 'Ajouter des destinataires'
|
||||||
add_recipients_description: 'Ajoutez de nouveaux destinataires pour que le document soit signé. Vous pouvez saisir leur adresse e-mail ou leur numéro de téléphone.'
|
add_recipients_description: "Ajoutez de nouveaux destinataires en saisissant leurs adresses e-mail ou numéros de téléphone pour faire signer le document."
|
||||||
settings: 'Paramètres'
|
settings: 'Paramètres'
|
||||||
settings_account_description: "Grâce à des paramètres de compte complets, vous pouvez personnaliser l'expérience de signature électronique et inviter d'autres utilisateurs."
|
settings_account_description: "Grâce à des paramètres de compte complets, vous pouvez personnaliser l'expérience de signature électronique et inviter d'autres utilisateurs."
|
||||||
support: 'Assistance'
|
support: 'Assistance'
|
||||||
@@ -3147,6 +3239,7 @@ fr: &fr
|
|||||||
read: Lire vos données
|
read: Lire vos données
|
||||||
|
|
||||||
pt: &pt
|
pt: &pt
|
||||||
|
private: Privado
|
||||||
resend_pending: Re-enviar pendente
|
resend_pending: Re-enviar pendente
|
||||||
ensure_unique_recipients: Garantir destinatários únicos
|
ensure_unique_recipients: Garantir destinatários únicos
|
||||||
require_phone_2fa_to_open: Necessário autenticação de dois fatores via telefone para abrir
|
require_phone_2fa_to_open: Necessário autenticação de dois fatores via telefone para abrir
|
||||||
@@ -3725,6 +3818,7 @@ pt: &pt
|
|||||||
not_invited_yet: Ainda não convidado
|
not_invited_yet: Ainda não convidado
|
||||||
not_completed_yet: Ainda não concluído
|
not_completed_yet: Ainda não concluído
|
||||||
declined_on_time: 'Recusado em %{time}'
|
declined_on_time: 'Recusado em %{time}'
|
||||||
|
expire_on_time: 'Expira em %{time}'
|
||||||
sign_in_person: Assinar pessoalmente
|
sign_in_person: Assinar pessoalmente
|
||||||
create_a_new_template_document_form_or_submit_the_existing_one_html: '<a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Criar um novo modelo</a> de documento ou <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">submeter o existente</a>'
|
create_a_new_template_document_form_or_submit_the_existing_one_html: '<a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Criar um novo modelo</a> de documento ou <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">submeter o existente</a>'
|
||||||
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Envie uma cópia do e-mail com documentos concluídos para um endereço BCC especificado.
|
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Envie uma cópia do e-mail com documentos concluídos para um endereço BCC especificado.
|
||||||
@@ -3845,6 +3939,25 @@ pt: &pt
|
|||||||
name_a_z: Nome A-Z
|
name_a_z: Nome A-Z
|
||||||
recently_used: Recentemente usado
|
recently_used: Recentemente usado
|
||||||
newest_first: Mais recente primeiro
|
newest_first: Mais recente primeiro
|
||||||
|
none: Nenhum
|
||||||
|
default_expiration: Expiração padrão
|
||||||
|
specified_date: Data especificada
|
||||||
|
one_day: 1 dia
|
||||||
|
two_days: 2 dias
|
||||||
|
three_days: 3 dias
|
||||||
|
four_days: 4 dias
|
||||||
|
five_days: 5 dias
|
||||||
|
six_days: 6 dias
|
||||||
|
seven_days: 7 dias
|
||||||
|
eight_days: 8 dias
|
||||||
|
nine_days: 9 dias
|
||||||
|
ten_days: 10 dias
|
||||||
|
two_weeks: 2 semanas
|
||||||
|
three_weeks: 3 semanas
|
||||||
|
four_weeks: 4 semanas
|
||||||
|
one_month: 1 mês
|
||||||
|
two_months: 2 meses
|
||||||
|
three_months: 3 meses
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Envio em massa
|
bulk: Envio em massa
|
||||||
@@ -3852,8 +3965,10 @@ pt: &pt
|
|||||||
invite: Convite
|
invite: Convite
|
||||||
link: Link
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-mail enviado</b> para %{submitter_name}'
|
send_email_to_html: '<b>Email enviado</b> para %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>E-mail de lembrete enviado</b> para %{submitter_name}'
|
bounce_email_html: '<b>Email não entregue</b> %{submitter_name}'
|
||||||
|
complaint_email_html: '<b>Reclamação de spam</b> %{submitter_name}'
|
||||||
|
send_reminder_email_to_html: '<b>Email de lembrete enviado</b> para %{submitter_name}'
|
||||||
send_sms_to_html: '<b>SMS enviado</b> para %{submitter_name}'
|
send_sms_to_html: '<b>SMS enviado</b> para %{submitter_name}'
|
||||||
send_2fa_sms_to_html: '<b>SMS de verificação enviado</b> para %{submitter_name}'
|
send_2fa_sms_to_html: '<b>SMS de verificação enviado</b> para %{submitter_name}'
|
||||||
open_email_by_html: '<b>E-mail aberto</b> por %{submitter_name}'
|
open_email_by_html: '<b>E-mail aberto</b> por %{submitter_name}'
|
||||||
@@ -3888,11 +4003,11 @@ pt: &pt
|
|||||||
previous: Anterior
|
previous: Anterior
|
||||||
next: Próximo
|
next: Próximo
|
||||||
template_and_submissions: 'Modelos e Envios'
|
template_and_submissions: 'Modelos e Envios'
|
||||||
template_and_submissions_description: "Você pode selecionar a visualização que melhor se adapta ao seu fluxo de trabalho. Escolha a visualização 'Modelos' se criar modelos de documentos reutilizáveis, ou 'Envios' ao assinar documentos individuais ou revisar o status de cada solicitação de assinatura."
|
template_and_submissions_description: "Você pode selecionar a visualização que melhor se adapta ao seu fluxo de trabalho. Escolha a visualização 'Modelos' para criar modelos reutilizáveis de documentos ou 'Envios' para assinar documentos individuais ou verificar o status de cada solicitação de assinatura."
|
||||||
upload_a_pdf_file: 'Enviar um arquivo PDF'
|
upload_a_pdf_file: 'Enviar um arquivo PDF'
|
||||||
upload_a_pdf_file_description: 'Envie um documento para usá-lo como modelo de formulário de assinatura.'
|
upload_a_pdf_file_description: 'Envie um documento PDF para criar um modelo de formulário de assinatura.'
|
||||||
select_a_signer_party: 'Selecionar parte assinante'
|
select_a_signer_party: 'Selecionar parte assinante'
|
||||||
select_a_signer_party_description: 'Este menu suspenso permite selecionar um papel de assinante ou adicionar uma nova parte ao documento quando ele precisa ser assinado por duas ou mais partes.'
|
select_a_signer_party_description: 'Este menu suspenso permite selecionar um papel de assinante ou adicionar uma nova parte ao documento, se forem necessárias 2 ou mais assinaturas.'
|
||||||
available_parties: 'Partes disponíveis'
|
available_parties: 'Partes disponíveis'
|
||||||
available_parties_description: 'Esta lista contém todas as partes disponíveis com seus respectivos papéis. Após selecionar, você poderá adicionar e configurar os campos para essa parte.'
|
available_parties_description: 'Esta lista contém todas as partes disponíveis com seus respectivos papéis. Após selecionar, você poderá adicionar e configurar os campos para essa parte.'
|
||||||
available_field_types: 'Tipos de campo disponíveis'
|
available_field_types: 'Tipos de campo disponíveis'
|
||||||
@@ -3900,7 +4015,7 @@ pt: &pt
|
|||||||
text_input_field: 'Campo de texto'
|
text_input_field: 'Campo de texto'
|
||||||
text_input_field_description: 'Este campo permite que os usuários insiram informações baseadas em texto, como nomes ou e-mails.'
|
text_input_field_description: 'Este campo permite que os usuários insiram informações baseadas em texto, como nomes ou e-mails.'
|
||||||
signature_field: 'Campo de assinatura'
|
signature_field: 'Campo de assinatura'
|
||||||
signature_field_description: 'Este campo permite coletar assinaturas dos signatários.'
|
signature_field_description: 'Este campo é usado para coletar assinaturas dos signatários.'
|
||||||
added_fields: 'Campos adicionados'
|
added_fields: 'Campos adicionados'
|
||||||
added_fields_description: 'Esta lista exibe todos os campos adicionados ao documento. Você pode reordená-los, editá-los ou removê-los.'
|
added_fields_description: 'Esta lista exibe todos os campos adicionados ao documento. Você pode reordená-los, editá-los ou removê-los.'
|
||||||
open_field_settings: 'Abrir configurações do campo'
|
open_field_settings: 'Abrir configurações do campo'
|
||||||
@@ -3917,7 +4032,7 @@ pt: &pt
|
|||||||
sign_the_document_description: 'Se você for um dos signatários, este botão permite que você assine o documento.'
|
sign_the_document_description: 'Se você for um dos signatários, este botão permite que você assine o documento.'
|
||||||
send_for_signing: 'Enviar para assinatura'
|
send_for_signing: 'Enviar para assinatura'
|
||||||
add_recipients: 'Adicionar destinatários'
|
add_recipients: 'Adicionar destinatários'
|
||||||
add_recipients_description: 'Adicione novos destinatários para que o documento seja assinado. Você pode inserir seus e-mails ou números de telefone.'
|
add_recipients_description: 'Adicione novos destinatários inserindo seus endereços de e-mail ou números de telefone para assinar o documento.'
|
||||||
settings: 'Configurações'
|
settings: 'Configurações'
|
||||||
settings_account_description: 'Com configurações completas de conta, você pode personalizar a experiência de assinatura eletrônica e convidar mais usuários.'
|
settings_account_description: 'Com configurações completas de conta, você pode personalizar a experiência de assinatura eletrônica e convidar mais usuários.'
|
||||||
support: 'Suporte'
|
support: 'Suporte'
|
||||||
@@ -3930,6 +4045,7 @@ pt: &pt
|
|||||||
read: Ler seus dados
|
read: Ler seus dados
|
||||||
|
|
||||||
de: &de
|
de: &de
|
||||||
|
private: Privat
|
||||||
resend_pending: Ausstehende erneut senden
|
resend_pending: Ausstehende erneut senden
|
||||||
ensure_unique_recipients: Stellen Sie einzigartige Empfänger sicher
|
ensure_unique_recipients: Stellen Sie einzigartige Empfänger sicher
|
||||||
require_phone_2fa_to_open: Telefon-2FA zum Öffnen erforderlich
|
require_phone_2fa_to_open: Telefon-2FA zum Öffnen erforderlich
|
||||||
@@ -4508,6 +4624,7 @@ de: &de
|
|||||||
not_invited_yet: Noch nicht eingeladen
|
not_invited_yet: Noch nicht eingeladen
|
||||||
not_completed_yet: Noch nicht abgeschlossen
|
not_completed_yet: Noch nicht abgeschlossen
|
||||||
declined_on_time: 'Abgelehnt am %{time}'
|
declined_on_time: 'Abgelehnt am %{time}'
|
||||||
|
expire_on_time: 'Ablauf am %{time}'
|
||||||
sign_in_person: Persönlich unterschreiben
|
sign_in_person: Persönlich unterschreiben
|
||||||
create_a_new_template_document_form_or_submit_the_existing_one_html: '<a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Neue Vorlage erstellen</a> oder <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">vorhandene einreichen</a>'
|
create_a_new_template_document_form_or_submit_the_existing_one_html: '<a href="%{new_template_link}" data-turbo-frame="modal" class="inline underline font-medium">Neue Vorlage erstellen</a> oder <a href="%{start_form_link}" target="_blank" class="inline underline font-medium">vorhandene einreichen</a>'
|
||||||
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Senden Sie eine E-Mail-Kopie mit abgeschlossenen Dokumenten an eine angegebene BCC-Adresse.
|
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Senden Sie eine E-Mail-Kopie mit abgeschlossenen Dokumenten an eine angegebene BCC-Adresse.
|
||||||
@@ -4628,6 +4745,25 @@ de: &de
|
|||||||
name_a_z: Name A-Z
|
name_a_z: Name A-Z
|
||||||
recently_used: Kürzlich verwendet
|
recently_used: Kürzlich verwendet
|
||||||
newest_first: Neueste zuerst
|
newest_first: Neueste zuerst
|
||||||
|
none: Keine
|
||||||
|
default_expiration: Standardablauf
|
||||||
|
specified_date: Angegebenes Datum
|
||||||
|
one_day: 1 Tag
|
||||||
|
two_days: 2 Tage
|
||||||
|
three_days: 3 Tage
|
||||||
|
four_days: 4 Tage
|
||||||
|
five_days: 5 Tage
|
||||||
|
six_days: 6 Tage
|
||||||
|
seven_days: 7 Tage
|
||||||
|
eight_days: 8 Tage
|
||||||
|
nine_days: 9 Tage
|
||||||
|
ten_days: 10 Tage
|
||||||
|
two_weeks: 2 Wochen
|
||||||
|
three_weeks: 3 Wochen
|
||||||
|
four_weeks: 4 Wochen
|
||||||
|
one_month: 1 Monat
|
||||||
|
two_months: 2 Monate
|
||||||
|
three_months: 3 Monate
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Massenversand
|
bulk: Massenversand
|
||||||
@@ -4636,6 +4772,8 @@ de: &de
|
|||||||
link: Link
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-Mail gesendet</b> an %{submitter_name}'
|
send_email_to_html: '<b>E-Mail gesendet</b> an %{submitter_name}'
|
||||||
|
bounce_email_html: '<b>E-Mail zurückgesendet</b> %{submitter_name}'
|
||||||
|
complaint_email_html: '<b>Spam-Beschwerde</b> %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>Erinnerungs-E-Mail gesendet</b> an %{submitter_name}'
|
send_reminder_email_to_html: '<b>Erinnerungs-E-Mail gesendet</b> an %{submitter_name}'
|
||||||
send_sms_to_html: '<b>SMS gesendet</b> an %{submitter_name}'
|
send_sms_to_html: '<b>SMS gesendet</b> an %{submitter_name}'
|
||||||
send_2fa_sms_to_html: '<b>Verifizierungs-SMS gesendet</b> an %{submitter_name}'
|
send_2fa_sms_to_html: '<b>Verifizierungs-SMS gesendet</b> an %{submitter_name}'
|
||||||
@@ -4671,11 +4809,11 @@ de: &de
|
|||||||
previous: Zurück
|
previous: Zurück
|
||||||
next: Weiter
|
next: Weiter
|
||||||
template_and_submissions: 'Vorlagen und Übermittlungen'
|
template_and_submissions: 'Vorlagen und Übermittlungen'
|
||||||
template_and_submissions_description: "Du kannst die Ansicht wählen, die am besten zu deinem Arbeitsablauf passt. Wähle die Ansicht 'Vorlagen', wenn du wiederverwendbare Dokumentenvorlagen erstellst, oder 'Übermittlungen', wenn du einzelne Dokumente unterschreibst oder den Status jeder Signaturanfrage überprüfen möchtest."
|
template_and_submissions_description: "Du kannst die Ansicht wählen, die am besten zu deinem Arbeitsablauf passt. Wähle die Ansicht 'Vorlagen', um wiederverwendbare Dokumentvorlagen zu erstellen, oder 'Übermittlungen', um einzelne Dokumente zu unterschreiben oder den Status jeder Signaturanfrage zu überprüfen."
|
||||||
upload_a_pdf_file: 'PDF-Datei hochladen'
|
upload_a_pdf_file: 'PDF-Datei hochladen'
|
||||||
upload_a_pdf_file_description: 'Lade ein Dokument hoch, um es als Vorlage für ein Signaturformular zu verwenden.'
|
upload_a_pdf_file_description: 'Lade ein PDF-Dokument hoch, um eine Vorlage für ein Signaturformular zu erstellen.'
|
||||||
select_a_signer_party: 'Signaturpartei auswählen'
|
select_a_signer_party: 'Signaturpartei auswählen'
|
||||||
select_a_signer_party_description: 'Dieses Dropdown-Menü ermöglicht es dir, eine Signaturrolle auszuwählen oder eine neue Partei zum Dokument hinzuzufügen, wenn es von zwei oder mehr Parteien unterzeichnet werden muss.'
|
select_a_signer_party_description: 'Dieses Dropdown-Menü ermöglicht es dir, eine Signaturrolle auszuwählen oder eine neue Partei zum Dokument hinzuzufügen, wenn es 2 oder mehr Unterschriften erfordert.'
|
||||||
available_parties: 'Verfügbare Parteien'
|
available_parties: 'Verfügbare Parteien'
|
||||||
available_parties_description: 'Diese Liste enthält alle verfügbaren Parteien mit ihren Rollennamen. Nach der Auswahl kannst du Felder für die Partei hinzufügen und konfigurieren.'
|
available_parties_description: 'Diese Liste enthält alle verfügbaren Parteien mit ihren Rollennamen. Nach der Auswahl kannst du Felder für die Partei hinzufügen und konfigurieren.'
|
||||||
available_field_types: 'Verfügbare Feldtypen'
|
available_field_types: 'Verfügbare Feldtypen'
|
||||||
@@ -4683,7 +4821,7 @@ de: &de
|
|||||||
text_input_field: 'Textfeld'
|
text_input_field: 'Textfeld'
|
||||||
text_input_field_description: 'Dieses Feld ermöglicht es Benutzern, textbasierte Informationen wie Namen oder E-Mails einzugeben.'
|
text_input_field_description: 'Dieses Feld ermöglicht es Benutzern, textbasierte Informationen wie Namen oder E-Mails einzugeben.'
|
||||||
signature_field: 'Signaturfeld'
|
signature_field: 'Signaturfeld'
|
||||||
signature_field_description: 'Dieses Feld dient der Erfassung von Signaturen der Unterzeichner.'
|
signature_field_description: 'Dieses Feld wird verwendet, um die Unterschriften der Unterzeichner zu erfassen.'
|
||||||
added_fields: 'Hinzugefügte Felder'
|
added_fields: 'Hinzugefügte Felder'
|
||||||
added_fields_description: 'Diese Liste zeigt alle dem Dokument hinzugefügten Felder. Du kannst sie neu anordnen, bearbeiten oder entfernen.'
|
added_fields_description: 'Diese Liste zeigt alle dem Dokument hinzugefügten Felder. Du kannst sie neu anordnen, bearbeiten oder entfernen.'
|
||||||
open_field_settings: 'Feld-Einstellungen öffnen'
|
open_field_settings: 'Feld-Einstellungen öffnen'
|
||||||
@@ -4697,10 +4835,10 @@ de: &de
|
|||||||
copy_and_share_link: 'Link kopieren und teilen'
|
copy_and_share_link: 'Link kopieren und teilen'
|
||||||
copy_and_share_link_description: 'Kopiere diesen Link, um das Dokument zu teilen. Jeder mit dem Link kann es unterschreiben, nachdem er seine E-Mail-Adresse eingegeben hat.'
|
copy_and_share_link_description: 'Kopiere diesen Link, um das Dokument zu teilen. Jeder mit dem Link kann es unterschreiben, nachdem er seine E-Mail-Adresse eingegeben hat.'
|
||||||
sign_the_document: 'Dokument unterschreiben'
|
sign_the_document: 'Dokument unterschreiben'
|
||||||
sign_the_document_description: 'Wenn du einer der Unterzeichner bist, kannst du das Dokument mit diesem Button selbst unterschreiben.'
|
sign_the_document_description: 'Wenn du einer der Unterzeichner bist, kannst du das Dokument mit diesem Button unterschreiben.'
|
||||||
send_for_signing: 'Zum Unterschreiben senden'
|
send_for_signing: 'Zum Unterschreiben senden'
|
||||||
add_recipients: 'Empfänger hinzufügen'
|
add_recipients: 'Empfänger hinzufügen'
|
||||||
add_recipients_description: 'Füge neue Empfänger hinzu, damit das Dokument unterschrieben werden kann. Du kannst deren E-Mail-Adressen oder Telefonnummern eingeben.'
|
add_recipients_description: 'Füge neue Empfänger hinzu, indem du ihre E-Mail-Adressen oder Telefonnummern eingibst, damit sie das Dokument unterschreiben können.'
|
||||||
settings: 'Einstellungen'
|
settings: 'Einstellungen'
|
||||||
settings_account_description: 'Mit umfangreichen Kontoeinstellungen kannst du das Signiererlebnis anpassen und weitere Benutzer einladen.'
|
settings_account_description: 'Mit umfangreichen Kontoeinstellungen kannst du das Signiererlebnis anpassen und weitere Benutzer einladen.'
|
||||||
support: 'Support'
|
support: 'Support'
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ Rails.application.routes.draw do
|
|||||||
resources :templates, only: %i[new create edit update show destroy] do
|
resources :templates, only: %i[new create edit update show destroy] do
|
||||||
resource :debug, only: %i[show], controller: 'templates_debug' if Rails.env.development?
|
resource :debug, only: %i[show], controller: 'templates_debug' if Rails.env.development?
|
||||||
resources :documents, only: %i[create], controller: 'template_documents'
|
resources :documents, only: %i[create], controller: 'template_documents'
|
||||||
|
resources :clone_and_replace, only: %i[create], controller: 'templates_clone_and_replace'
|
||||||
resources :restore, only: %i[create], controller: 'templates_restore'
|
resources :restore, only: %i[create], controller: 'templates_restore'
|
||||||
resources :archived, only: %i[index], controller: 'templates_archived_submissions'
|
resources :archived, only: %i[index], controller: 'templates_archived_submissions'
|
||||||
resources :submissions, only: %i[new create]
|
resources :submissions, only: %i[new create]
|
||||||
|
|||||||
@@ -5,26 +5,31 @@ module Abilities
|
|||||||
module_function
|
module_function
|
||||||
|
|
||||||
def collection(user, ability: nil)
|
def collection(user, ability: nil)
|
||||||
template_ids = Template.where(account_id: user.account_id).select(:id)
|
templates = Template.where(account_id: user.account_id)
|
||||||
|
|
||||||
|
return templates unless user.account.testing?
|
||||||
|
|
||||||
shared_ids =
|
shared_ids =
|
||||||
TemplateSharing.where({ ability:,
|
TemplateSharing.where({ ability:, account_id: [user.account_id, TemplateSharing::ALL_ID] }.compact)
|
||||||
account_id: [user.account_id, TemplateSharing::ALL_ID] }.compact)
|
|
||||||
.select(:template_id)
|
.select(:template_id)
|
||||||
|
|
||||||
join_query = Template.arel_table
|
join_query =
|
||||||
.join(Arel::Nodes::TableAlias.new(template_ids.arel.union(shared_ids.arel), 'union_ids'))
|
Template.arel_table
|
||||||
.on(Template.arel_table[:id].eq(Arel::Table.new(:union_ids)[:id]))
|
.join(Arel::Nodes::TableAlias.new(templates.select(:id).arel.union(shared_ids.arel), 'union_ids'))
|
||||||
|
.on(Template.arel_table[:id].eq(Arel::Table.new(:union_ids)[:id]))
|
||||||
|
|
||||||
Template.joins(join_query.join_sources.first)
|
Template.joins(join_query.join_sources.first)
|
||||||
end
|
end
|
||||||
|
|
||||||
def entity(template, user:, ability: nil)
|
def entity(template, user:, ability: nil)
|
||||||
|
return true if template.account_id.blank?
|
||||||
return true if template.account_id == user.account_id
|
return true if template.account_id == user.account_id
|
||||||
|
return false unless user.account.linked_account_account
|
||||||
|
return false if template.template_sharings.to_a.blank?
|
||||||
|
|
||||||
account_ids = [user.account_id, TemplateSharing::ALL_ID]
|
account_ids = [user.account_id, TemplateSharing::ALL_ID]
|
||||||
|
|
||||||
template.template_sharings.any? do |e|
|
template.template_sharings.to_a.any? do |e|
|
||||||
e.account_id.in?(account_ids) && (ability.nil? || e.ability == 'manage' || e.ability == ability)
|
e.account_id.in?(account_ids) && (ability.nil? || e.ability == 'manage' || e.ability == ability)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ class Ability
|
|||||||
include CanCan::Ability
|
include CanCan::Ability
|
||||||
|
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
can :manage, Template, account_id: user.account_id
|
can %i[read create update], Template, Abilities::TemplateConditions.collection(user) do |template|
|
||||||
|
|
||||||
can %i[read update create], Template,
|
|
||||||
Abilities::TemplateConditions.collection(user) do |template|
|
|
||||||
Abilities::TemplateConditions.entity(template, user:, ability: 'manage')
|
Abilities::TemplateConditions.entity(template, user:, ability: 'manage')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
can :destroy, Template, account_id: user.account_id
|
||||||
can :manage, TemplateFolder, account_id: user.account_id
|
can :manage, TemplateFolder, account_id: user.account_id
|
||||||
can :manage, TemplateSharing, template: { account_id: user.account_id }
|
can :manage, TemplateSharing, template: { account_id: user.account_id }
|
||||||
can :manage, Submission, account_id: user.account_id
|
can :manage, Submission, account_id: user.account_id
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ module Docuseal
|
|||||||
ENV['MULTITENANT'] == 'true'
|
ENV['MULTITENANT'] == 'true'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def advanced_formats?
|
||||||
|
multitenant?
|
||||||
|
end
|
||||||
|
|
||||||
def demo?
|
def demo?
|
||||||
ENV['DEMO'] == 'true'
|
ENV['DEMO'] == 'true'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ module ReplaceEmailVariables
|
|||||||
SUBMITTER_SLUG = /\{+submitter\.slug\}+/i
|
SUBMITTER_SLUG = /\{+submitter\.slug\}+/i
|
||||||
SUBMISSION_LINK = /\{+submission\.link\}+/i
|
SUBMISSION_LINK = /\{+submission\.link\}+/i
|
||||||
SUBMISSION_ID = /\{+submission\.id\}+/i
|
SUBMISSION_ID = /\{+submission\.id\}+/i
|
||||||
|
SUBMISSION_EXPIRE_AT = /\{+submission\.expire_at\}+/i
|
||||||
SUBMITTERS = /\{+(?:submission\.)?submitters\}+/i
|
SUBMITTERS = /\{+(?:submission\.)?submitters\}+/i
|
||||||
SUBMITTERS_N_EMAIL = /\{+submitters\[(?<index>\d+)\]\.email\}+/i
|
SUBMITTERS_N_EMAIL = /\{+submitters\[(?<index>\d+)\]\.email\}+/i
|
||||||
SUBMITTERS_N_NAME = /\{+submitters\[(?<index>\d+)\]\.name\}+/i
|
SUBMITTERS_N_NAME = /\{+submitters\[(?<index>\d+)\]\.name\}+/i
|
||||||
@@ -48,6 +49,13 @@ module ReplaceEmailVariables
|
|||||||
text = replace(text, SENDER_NAME, html_escape:) { submitter.submission.created_by_user&.full_name }
|
text = replace(text, SENDER_NAME, html_escape:) { submitter.submission.created_by_user&.full_name }
|
||||||
text = replace(text, SENDER_FIRST_NAME, html_escape:) { submitter.submission.created_by_user&.first_name }
|
text = replace(text, SENDER_FIRST_NAME, html_escape:) { submitter.submission.created_by_user&.first_name }
|
||||||
|
|
||||||
|
text = replace(text, SUBMISSION_EXPIRE_AT, html_escape:) do
|
||||||
|
if submitter.submission.expire_at
|
||||||
|
I18n.l(submitter.submission.expire_at.in_time_zone(submitter.submission.account.timezone),
|
||||||
|
format: :short, locale: submitter.submission.account.locale)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
text = replace(text, SUBMITTERS_N_NAME, html_escape:) do |match|
|
text = replace(text, SUBMITTERS_N_NAME, html_escape:) do |match|
|
||||||
build_submitters_n_field(submitter.submission, match[:index].to_i - 1, :name)
|
build_submitters_n_field(submitter.submission, match[:index].to_i - 1, :name)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -59,10 +59,13 @@ module Submissions
|
|||||||
def create_from_emails(template:, user:, emails:, source:, mark_as_sent: false, params: {})
|
def create_from_emails(template:, user:, emails:, source:, mark_as_sent: false, params: {})
|
||||||
preferences = Submitters.normalize_preferences(user.account, user, params)
|
preferences = Submitters.normalize_preferences(user.account, user, params)
|
||||||
|
|
||||||
|
expire_at = params[:expire_at].presence || Templates.build_default_expire_at(template)
|
||||||
|
|
||||||
parse_emails(emails, user).uniq.map do |email|
|
parse_emails(emails, user).uniq.map do |email|
|
||||||
submission = template.submissions.new(created_by_user: user,
|
submission = template.submissions.new(created_by_user: user,
|
||||||
account_id: user.account_id,
|
account_id: user.account_id,
|
||||||
source:,
|
source:,
|
||||||
|
expire_at:,
|
||||||
template_submitters: template.submitters)
|
template_submitters: template.submitters)
|
||||||
|
|
||||||
submission.submitters.new(email: normalize_email(email),
|
submission.submitters.new(email: normalize_email(email),
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ module Submissions
|
|||||||
|
|
||||||
set_submission_preferences = submission_preferences.slice('send_email', 'bcc_completed')
|
set_submission_preferences = submission_preferences.slice('send_email', 'bcc_completed')
|
||||||
set_submission_preferences['send_email'] = true if params['send_completed_email']
|
set_submission_preferences['send_email'] = true if params['send_completed_email']
|
||||||
|
expire_at = attrs[:expire_at] || Templates.build_default_expire_at(template)
|
||||||
|
|
||||||
submission = template.submissions.new(created_by_user: user, source:,
|
submission = template.submissions.new(created_by_user: user, source:,
|
||||||
account_id: user.account_id,
|
account_id: user.account_id,
|
||||||
preferences: set_submission_preferences,
|
preferences: set_submission_preferences,
|
||||||
expire_at: attrs[:expire_at],
|
expire_at:,
|
||||||
template_submitters: [], submitters_order:)
|
template_submitters: [], submitters_order:)
|
||||||
|
|
||||||
template_submitters = template.submitters.deep_dup
|
template_submitters = template.submitters.deep_dup
|
||||||
|
|||||||
@@ -379,7 +379,9 @@ module Submissions
|
|||||||
|
|
||||||
composer.text(I18n.t('event_log'), font_size: 12, padding: [10, 0, 20, 0])
|
composer.text(I18n.t('event_log'), font_size: 12, padding: [10, 0, 20, 0])
|
||||||
|
|
||||||
events_data = submission.submission_events.sort_by(&:event_timestamp).map do |event|
|
events_data = submission.submission_events.sort_by(&:event_timestamp).filter_map do |event|
|
||||||
|
next if event.event_type.in?(%w[bounce_email complaint_email])
|
||||||
|
|
||||||
submitter = submission.submitters.find { |e| e.id == event.submitter_id }
|
submitter = submission.submitters.find { |e| e.id == event.submitter_id }
|
||||||
submitter_name =
|
submitter_name =
|
||||||
if event.event_type.include?('sms') || event.event_type.include?('phone')
|
if event.event_type.include?('sms') || event.event_type.include?('phone')
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ module Submissions
|
|||||||
font_size = preferences_font_size
|
font_size = preferences_font_size
|
||||||
font_size ||= (([page.box.width, page.box.height].min / A4_SIZE[0].to_f) * FONT_SIZE).to_i
|
font_size ||= (([page.box.width, page.box.height].min / A4_SIZE[0].to_f) * FONT_SIZE).to_i
|
||||||
|
|
||||||
fill_color = field.dig('preferences', 'color').presence
|
fill_color = field.dig('preferences', 'color').to_s.delete_prefix('#').presence
|
||||||
|
|
||||||
font_name = field.dig('preferences', 'font')
|
font_name = field.dig('preferences', 'font')
|
||||||
font_variant = (field.dig('preferences', 'font_type').presence || 'none').to_sym
|
font_variant = (field.dig('preferences', 'font_type').presence || 'none').to_sym
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
module Submissions
|
module Submissions
|
||||||
class TimestampHandler
|
class TimestampHandler
|
||||||
HASH_ALGORITHM = 'SHA512'
|
HASH_ALGORITHM = 'SHA256'
|
||||||
|
|
||||||
TimestampError = Class.new(StandardError)
|
TimestampError = Class.new(StandardError)
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ module Submissions
|
|||||||
signature[:SubFilter] = :'ETSI.RFC3161'
|
signature[:SubFilter] = :'ETSI.RFC3161'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# rubocop:disable Metrics
|
||||||
def sign(io, byte_range)
|
def sign(io, byte_range)
|
||||||
digest = OpenSSL::Digest.new(HASH_ALGORITHM)
|
digest = OpenSSL::Digest.new(HASH_ALGORITHM)
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ module Submissions
|
|||||||
c.basic_auth(uri.user, uri.password) if uri.password.present?
|
c.basic_auth(uri.user, uri.password) if uri.password.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
response = conn.post(uri.path, build_payload(digest.digest),
|
response = conn.post(uri.request_uri, build_payload(digest.digest),
|
||||||
'content-type' => 'application/timestamp-query')
|
'content-type' => 'application/timestamp-query')
|
||||||
|
|
||||||
if response.status != 200 || response.body.blank?
|
if response.status != 200 || response.body.blank?
|
||||||
@@ -49,7 +50,13 @@ module Submissions
|
|||||||
end
|
end
|
||||||
|
|
||||||
OpenSSL::Timestamp::Response.new(response.body).token.to_der
|
OpenSSL::Timestamp::Response.new(response.body).token.to_der
|
||||||
|
rescue StandardError => e
|
||||||
|
Rollbar.error(e) if defined?(Rollbar)
|
||||||
|
Rails.logger.error(e)
|
||||||
|
|
||||||
|
OpenSSL::ASN1::GeneralizedTime.new(Time.now.utc).to_der
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics
|
||||||
|
|
||||||
def build_payload(digest)
|
def build_payload(digest)
|
||||||
req = OpenSSL::Timestamp::Request.new
|
req = OpenSSL::Timestamp::Request.new
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ module Submitters
|
|||||||
additional_attrs['fields'] = SerializeForWebhook.build_fields_array(submitter)
|
additional_attrs['fields'] = SerializeForWebhook.build_fields_array(submitter)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if with_template
|
||||||
|
additional_attrs['template'] = submitter.submission.template.as_json(only: %i[id name created_at updated_at])
|
||||||
|
end
|
||||||
|
|
||||||
additional_attrs['values'] = SerializeForWebhook.build_values_array(submitter) if with_values
|
additional_attrs['values'] = SerializeForWebhook.build_values_array(submitter) if with_values
|
||||||
additional_attrs['documents'] = SerializeForWebhook.build_documents_array(submitter) if with_documents
|
additional_attrs['documents'] = SerializeForWebhook.build_documents_array(submitter) if with_documents
|
||||||
additional_attrs['preferences'] = submitter.preferences.except('default_values')
|
additional_attrs['preferences'] = submitter.preferences.except('default_values')
|
||||||
@@ -41,10 +45,7 @@ module Submitters
|
|||||||
Rails.application.routes.url_helpers.submit_form_url(slug: submitter.slug, **Docuseal.default_url_options)
|
Rails.application.routes.url_helpers.submit_form_url(slug: submitter.slug, **Docuseal.default_url_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
include_params = {}
|
submitter.as_json(SERIALIZE_PARAMS).merge(additional_attrs)
|
||||||
include_params[:template] = { only: %i[id name created_at updated_at] } if with_template
|
|
||||||
|
|
||||||
submitter.as_json(SERIALIZE_PARAMS.merge(include: include_params)).merge(additional_attrs)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_events(events)
|
def serialize_events(events)
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ module Submitters
|
|||||||
'audit_log_url' => submitter.submission.audit_log_url,
|
'audit_log_url' => submitter.submission.audit_log_url,
|
||||||
'submission_url' => r.submissions_preview_url(submitter.submission.slug,
|
'submission_url' => r.submissions_preview_url(submitter.submission.slug,
|
||||||
**Docuseal.default_url_options),
|
**Docuseal.default_url_options),
|
||||||
'template' => submitter.template.as_json(only: %i[id name external_id created_at updated_at],
|
'template' => submitter.submission.template.as_json(
|
||||||
methods: %i[folder_name]),
|
only: %i[id name external_id created_at updated_at],
|
||||||
|
methods: %i[folder_name]
|
||||||
|
),
|
||||||
'submission' => {
|
'submission' => {
|
||||||
**submitter.submission.slice(:id, :audit_log_url, :combined_document_url, :created_at),
|
**submitter.submission.slice(:id, :audit_log_url, :combined_document_url, :created_at),
|
||||||
status: build_submission_status(submitter.submission),
|
status: build_submission_status(submitter.submission),
|
||||||
|
|||||||
@@ -1,6 +1,25 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Templates
|
module Templates
|
||||||
|
EXPIRATION_DURATIONS = {
|
||||||
|
one_day: 1.day,
|
||||||
|
two_days: 2.days,
|
||||||
|
three_days: 3.days,
|
||||||
|
four_days: 4.days,
|
||||||
|
five_days: 5.days,
|
||||||
|
six_days: 6.days,
|
||||||
|
seven_days: 7.days,
|
||||||
|
eight_days: 8.days,
|
||||||
|
nine_days: 9.days,
|
||||||
|
ten_days: 10.days,
|
||||||
|
two_weeks: 14.days,
|
||||||
|
three_weeks: 21.days,
|
||||||
|
four_weeks: 28.days,
|
||||||
|
one_month: 1.month,
|
||||||
|
two_months: 2.months,
|
||||||
|
three_months: 3.months
|
||||||
|
}.with_indifferent_access.freeze
|
||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def build_field_areas_index(fields)
|
def build_field_areas_index(fields)
|
||||||
@@ -30,4 +49,17 @@ module Templates
|
|||||||
item['linked_to_uuid'].blank? && item['is_requester'].blank? && item['email'].blank?
|
item['linked_to_uuid'].blank? && item['is_requester'].blank? && item['email'].blank?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_default_expire_at(template)
|
||||||
|
default_expire_at_duration = template.preferences['default_expire_at_duration'].presence
|
||||||
|
default_expire_at = template.preferences['default_expire_at'].presence
|
||||||
|
|
||||||
|
return if default_expire_at_duration.blank?
|
||||||
|
|
||||||
|
if default_expire_at_duration == 'specified_date' && default_expire_at.present?
|
||||||
|
Time.zone.parse(default_expire_at)
|
||||||
|
elsif EXPIRATION_DURATIONS[default_expire_at_duration]
|
||||||
|
Time.current + EXPIRATION_DURATIONS[default_expire_at_duration]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module Templates
|
|||||||
module Clone
|
module Clone
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
|
# rubocop:disable Metrics, Style/CombinableLoops
|
||||||
def call(original_template, author:, external_id: nil, name: nil, folder_name: nil)
|
def call(original_template, author:, external_id: nil, name: nil, folder_name: nil)
|
||||||
template = original_template.account.templates.new
|
template = original_template.account.templates.new
|
||||||
|
|
||||||
@@ -29,10 +30,13 @@ module Templates
|
|||||||
template.schema.first['name'] = template.name
|
template.schema.first['name'] = template.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
original_template.template_accesses.each do |template_access|
|
||||||
|
template.template_accesses.new(user_id: template_access.user_id)
|
||||||
|
end
|
||||||
|
|
||||||
template
|
template
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop:disable Metrics, Style/CombinableLoops
|
|
||||||
def update_submitters_and_fields_and_schema(cloned_submitters, cloned_fields, cloned_schema, cloned_preferences)
|
def update_submitters_and_fields_and_schema(cloned_submitters, cloned_fields, cloned_schema, cloned_preferences)
|
||||||
submitter_uuids_replacements = {}
|
submitter_uuids_replacements = {}
|
||||||
field_uuids_replacements = {}
|
field_uuids_replacements = {}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ module Templates
|
|||||||
module CloneAttachments
|
module CloneAttachments
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def call(template:, original_template:, documents: [])
|
def call(template:, original_template:, documents: [], excluded_attachment_uuids: [])
|
||||||
schema_uuids_replacements = {}
|
schema_uuids_replacements = {}
|
||||||
|
|
||||||
template.schema.each_with_index do |schema_item, index|
|
template.schema.each_with_index do |schema_item, index|
|
||||||
|
next if excluded_attachment_uuids.include?(schema_item['attachment_uuid'])
|
||||||
|
|
||||||
new_schema_item_uuid = SecureRandom.uuid
|
new_schema_item_uuid = SecureRandom.uuid
|
||||||
|
|
||||||
schema_uuids_replacements[schema_item['attachment_uuid']] = new_schema_item_uuid
|
schema_uuids_replacements[schema_item['attachment_uuid']] = new_schema_item_uuid
|
||||||
@@ -22,17 +24,22 @@ module Templates
|
|||||||
next if field['areas'].blank?
|
next if field['areas'].blank?
|
||||||
|
|
||||||
field['areas'].each do |area|
|
field['areas'].each do |area|
|
||||||
area['attachment_uuid'] = schema_uuids_replacements[area['attachment_uuid']]
|
new_attachment_uuid = schema_uuids_replacements[area['attachment_uuid']]
|
||||||
|
area['attachment_uuid'] = new_attachment_uuid if new_attachment_uuid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
template.save!
|
template.save!
|
||||||
|
|
||||||
original_template.schema_documents.map do |document|
|
original_template.schema_documents.filter_map do |document|
|
||||||
|
new_attachment_uuid = schema_uuids_replacements[document.uuid]
|
||||||
|
|
||||||
|
next unless new_attachment_uuid
|
||||||
|
|
||||||
new_document =
|
new_document =
|
||||||
ApplicationRecord.no_touching do
|
ApplicationRecord.no_touching do
|
||||||
template.documents_attachments.create!(
|
template.documents_attachments.create!(
|
||||||
uuid: schema_uuids_replacements[document.uuid],
|
uuid: new_attachment_uuid,
|
||||||
blob_id: document.blob_id
|
blob_id: document.blob_id
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ module Templates
|
|||||||
options: build_options(field.allowed_values, 'radio'),
|
options: build_options(field.allowed_values, 'radio'),
|
||||||
default_value: selected_option
|
default_value: selected_option
|
||||||
}
|
}
|
||||||
elsif field.field_type == :Btn && field.concrete_field_type == :check_box
|
elsif field.field_type == :Btn && %i[check_box radio_button].include?(field.concrete_field_type)
|
||||||
{
|
{
|
||||||
**attrs,
|
**attrs,
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Templates
|
||||||
|
module ReplaceAttachments
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# rubocop:disable Metrics
|
||||||
|
def call(template, params = {}, extract_fields: false)
|
||||||
|
documents = Templates::CreateAttachments.call(template, params, extract_fields:)
|
||||||
|
submitter = template.submitters.first
|
||||||
|
|
||||||
|
documents.each_with_index do |document, index|
|
||||||
|
replaced_document_schema = template.schema[index]
|
||||||
|
|
||||||
|
template.schema[index] = { attachment_uuid: document.uuid, name: document.filename.base }
|
||||||
|
|
||||||
|
if replaced_document_schema
|
||||||
|
template.fields.each do |field|
|
||||||
|
next if field['areas'].blank?
|
||||||
|
|
||||||
|
field['areas'].each do |area|
|
||||||
|
if area['attachment_uuid'] == replaced_document_schema['attachment_uuid']
|
||||||
|
area['attachment_uuid'] = document.uuid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
next if template.fields.any? { |f| f['areas']&.any? { |a| a['attachment_uuid'] == document.uuid } }
|
||||||
|
next if submitter.blank? || document.metadata.dig('pdf', 'fields').blank?
|
||||||
|
|
||||||
|
pdf_fields = document.metadata['pdf'].delete('fields').to_a
|
||||||
|
pdf_fields.each { |f| f['submitter_uuid'] = submitter['uuid'] }
|
||||||
|
|
||||||
|
if index.positive? && pdf_fields.present?
|
||||||
|
preview_document = template.schema[index - 1]
|
||||||
|
preview_document_last_field = template.fields.reverse.find do |f|
|
||||||
|
f['areas']&.any? do |a|
|
||||||
|
a['attachment_uuid'] == preview_document[:attachment_uuid]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if preview_document_last_field
|
||||||
|
last_preview_document_field_index = template.fields.find_index do |f|
|
||||||
|
f['uuid'] == preview_document_last_field['uuid']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if last_preview_document_field_index
|
||||||
|
template.fields.insert(index, *pdf_fields)
|
||||||
|
else
|
||||||
|
template.fields += pdf_fields
|
||||||
|
end
|
||||||
|
elsif pdf_fields.present?
|
||||||
|
template.fields += pdf_fields
|
||||||
|
|
||||||
|
template.schema[index]['pending_fields'] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
documents
|
||||||
|
end
|
||||||
|
# rubocop:enable Metrics
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -47,7 +47,7 @@ module SigningFormHelper
|
|||||||
drawStep();
|
drawStep();
|
||||||
JS
|
JS
|
||||||
|
|
||||||
sleep 0.5
|
sleep 0.1
|
||||||
end
|
end
|
||||||
|
|
||||||
def field_value(submitter, field_name)
|
def field_value(submitter, field_name)
|
||||||
|
|||||||