Compare commits

..

No commits in common. 'a2d8b855491793870b7b4acf176d2d95ae95ff83' and '7ce6c29f4dfe62beceb97a21b6f49b995488ec1e' have entirely different histories.

@ -1,31 +1,31 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
action_text-trix (2.1.19) action_text-trix (2.1.18)
railties railties
actioncable (8.1.3.1) actioncable (8.1.3)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (8.1.3.1) actionmailbox (8.1.3)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
activejob (= 8.1.3.1) activejob (= 8.1.3)
activerecord (= 8.1.3.1) activerecord (= 8.1.3)
activestorage (= 8.1.3.1) activestorage (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
mail (>= 2.8.0) mail (>= 2.8.0)
actionmailer (8.1.3.1) actionmailer (8.1.3)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
actionview (= 8.1.3.1) actionview (= 8.1.3)
activejob (= 8.1.3.1) activejob (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
mail (>= 2.8.0) mail (>= 2.8.0)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (8.1.3.1) actionpack (8.1.3)
actionview (= 8.1.3.1) actionview (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
rack (>= 2.2.4) rack (>= 2.2.4)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
@ -33,36 +33,36 @@ GEM
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
useragent (~> 0.16) useragent (~> 0.16)
actiontext (8.1.3.1) actiontext (8.1.3)
action_text-trix (~> 2.1.15) action_text-trix (~> 2.1.15)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
activerecord (= 8.1.3.1) activerecord (= 8.1.3)
activestorage (= 8.1.3.1) activestorage (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (8.1.3.1) actionview (8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
activejob (8.1.3.1) activejob (8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (8.1.3.1) activemodel (8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
activerecord (8.1.3.1) activerecord (8.1.3)
activemodel (= 8.1.3.1) activemodel (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (8.1.3.1) activestorage (8.1.3)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
activejob (= 8.1.3.1) activejob (= 8.1.3)
activerecord (= 8.1.3.1) activerecord (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (8.1.3.1) activesupport (8.1.3)
base64 base64
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1) concurrent-ruby (~> 1.0, >= 1.3.1)
@ -144,7 +144,7 @@ GEM
cldr-plurals-runtime-rb (1.1.0) cldr-plurals-runtime-rb (1.1.0)
cmdparse (3.0.7) cmdparse (3.0.7)
coderay (1.1.3) coderay (1.1.3)
concurrent-ruby (1.3.8) concurrent-ruby (1.3.7)
connection_pool (3.0.2) connection_pool (3.0.2)
crack (1.0.1) crack (1.0.1)
bigdecimal bigdecimal
@ -179,7 +179,7 @@ GEM
dotenv (3.2.0) dotenv (3.2.0)
drb (2.2.3) drb (2.2.3)
email_typo (0.2.3) email_typo (0.2.3)
erb (6.0.6) erb (6.0.4)
erb_lint (0.9.0) erb_lint (0.9.0)
activesupport activesupport
better_html (>= 2.0.1) better_html (>= 2.0.1)
@ -217,7 +217,7 @@ GEM
foreman (0.90.0) foreman (0.90.0)
thor (~> 1.4) thor (~> 1.4)
geom2d (0.4.1) geom2d (0.4.1)
globalid (1.4.0) globalid (1.3.0)
activesupport (>= 6.1) activesupport (>= 6.1)
google-apis-core (1.0.2) google-apis-core (1.0.2)
addressable (~> 2.8, >= 2.8.7) addressable (~> 2.8, >= 2.8.7)
@ -262,7 +262,7 @@ GEM
geom2d (~> 0.4, >= 0.4.1) geom2d (~> 0.4, >= 0.4.1)
openssl (>= 2.2.1) openssl (>= 2.2.1)
strscan (>= 3.1.2) strscan (>= 3.1.2)
i18n (1.15.2) i18n (1.14.8)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
io-console (0.8.2) io-console (0.8.2)
irb (1.18.0) irb (1.18.0)
@ -271,7 +271,7 @@ GEM
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jmespath (1.6.2) jmespath (1.6.2)
json (2.21.1) json (2.19.9)
jwt (3.2.0) jwt (3.2.0)
base64 base64
language_server-protocol (3.17.0.5) language_server-protocol (3.17.0.5)
@ -296,24 +296,24 @@ GEM
loofah (2.25.2) loofah (2.25.2)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.9.1) mail (2.9.0)
logger logger
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
marcel (1.2.1) marcel (1.1.0)
matrix (0.4.3) matrix (0.4.3)
method_source (1.1.0) method_source (1.1.0)
mini_mime (1.1.5) mini_mime (1.1.5)
minitest (6.0.6) minitest (6.0.6)
drb (~> 2.0) drb (~> 2.0)
prism (~> 1.5) prism (~> 1.5)
msgpack (1.8.4) msgpack (1.8.0)
multi_json (1.19.1) multi_json (1.19.1)
net-http (0.9.1) net-http (0.9.1)
uri (>= 0.11.1) uri (>= 0.11.1)
net-imap (0.6.6) net-imap (0.6.4.1)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
@ -344,7 +344,7 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
os (1.1.4) os (1.1.4)
package_json (0.2.0) package_json (0.2.0)
pagy (43.6.1) pagy (43.4.4)
json json
uri uri
yaml yaml
@ -357,7 +357,7 @@ GEM
pg (1.6.3-arm64-darwin) pg (1.6.3-arm64-darwin)
pg (1.6.3-x86_64-linux) pg (1.6.3-x86_64-linux)
pg (1.6.3-x86_64-linux-musl) pg (1.6.3-x86_64-linux-musl)
pp (0.6.4) pp (0.6.3)
prettyprint prettyprint
pretender (1.0.0) pretender (1.0.0)
actionpack (>= 7.2) actionpack (>= 7.2)
@ -369,6 +369,9 @@ GEM
reline (>= 0.6.0) reline (>= 0.6.0)
pry-rails (0.3.11) pry-rails (0.3.11)
pry (>= 0.13.0) pry (>= 0.13.0)
psych (5.3.1)
date
stringio
public_suffix (7.0.5) public_suffix (7.0.5)
puma (8.0.2) puma (8.0.2)
nio4r (~> 2.0) nio4r (~> 2.0)
@ -383,20 +386,20 @@ GEM
rack (>= 1.3) rack (>= 1.3)
rackup (2.3.1) rackup (2.3.1)
rack (>= 3) rack (>= 3)
rails (8.1.3.1) rails (8.1.3)
actioncable (= 8.1.3.1) actioncable (= 8.1.3)
actionmailbox (= 8.1.3.1) actionmailbox (= 8.1.3)
actionmailer (= 8.1.3.1) actionmailer (= 8.1.3)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
actiontext (= 8.1.3.1) actiontext (= 8.1.3)
actionview (= 8.1.3.1) actionview (= 8.1.3)
activejob (= 8.1.3.1) activejob (= 8.1.3)
activemodel (= 8.1.3.1) activemodel (= 8.1.3)
activerecord (= 8.1.3.1) activerecord (= 8.1.3)
activestorage (= 8.1.3.1) activestorage (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 8.1.3.1) railties (= 8.1.3)
rails-dom-testing (2.3.0) rails-dom-testing (2.3.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
@ -407,9 +410,9 @@ GEM
rails-i18n (8.1.0) rails-i18n (8.1.0)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 8.0.0, < 9) railties (>= 8.0.0, < 9)
railties (8.1.3.1) railties (8.1.3)
actionpack (= 8.1.3.1) actionpack (= 8.1.3)
activesupport (= 8.1.3.1) activesupport (= 8.1.3)
irb (~> 1.13) irb (~> 1.13)
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
@ -418,14 +421,9 @@ GEM
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.4.2) rake (13.4.2)
rbs (4.1.0) rdoc (7.2.0)
logger
prism (>= 1.6.0)
tsort
rdoc (8.0.0)
erb erb
prism (>= 1.6.0) psych (>= 4.0.0)
rbs (>= 4.0.0)
tsort tsort
redis-client (0.29.0) redis-client (0.29.0)
connection_pool connection_pool
@ -527,11 +525,12 @@ GEM
simplecov-html (0.13.2) simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
smart_properties (1.17.0) smart_properties (1.17.0)
sqlite3 (2.9.5-aarch64-linux-gnu) sqlite3 (2.9.2-aarch64-linux-gnu)
sqlite3 (2.9.5-aarch64-linux-musl) sqlite3 (2.9.2-aarch64-linux-musl)
sqlite3 (2.9.5-arm64-darwin) sqlite3 (2.9.2-arm64-darwin)
sqlite3 (2.9.5-x86_64-linux-gnu) sqlite3 (2.9.2-x86_64-linux-gnu)
sqlite3 (2.9.5-x86_64-linux-musl) sqlite3 (2.9.2-x86_64-linux-musl)
stringio (3.2.0)
strip_attributes (2.0.1) strip_attributes (2.0.1)
activemodel (>= 3.0, < 9.0) activemodel (>= 3.0, < 9.0)
strscan (3.1.8) strscan (3.1.8)
@ -571,14 +570,14 @@ GEM
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.9.2) webrick (1.9.2)
websocket-driver (0.8.2) websocket-driver (0.8.0)
base64 base64
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
yaml (0.4.0) yaml (0.4.0)
zeitwerk (2.8.2) zeitwerk (2.7.5)
PLATFORMS PLATFORMS
aarch64-linux aarch64-linux

@ -37,7 +37,7 @@ class AccountConfigsController < ApplicationController
end end
def destroy def destroy
raise InvalidKey unless allowed_destroy_keys.include?(@account_config.key) raise InvalidKey unless allowed_keys.include?(@account_config.key)
@account_config.destroy! @account_config.destroy!
@ -50,10 +50,6 @@ class AccountConfigsController < ApplicationController
ALLOWED_KEYS ALLOWED_KEYS
end end
def allowed_destroy_keys
ALLOWED_KEYS
end
def load_account_config def load_account_config
raise InvalidKey unless allowed_keys.include?(account_config_params[:key]) raise InvalidKey unless allowed_keys.include?(account_config_params[:key])

@ -20,8 +20,6 @@ class MfaSetupController < ApplicationController
redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_configured') redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_configured')
else else
RateLimit.call("mfa-setup-otp-#{current_user.id}", limit: 5, ttl: 5.minutes, enabled: true)
@provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name) @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name)
@error_message = I18n.t('code_is_invalid') @error_message = I18n.t('code_is_invalid')
@ -36,8 +34,6 @@ class MfaSetupController < ApplicationController
redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_removed') redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_removed')
else else
RateLimit.call("mfa-setup-otp-#{current_user.id}", limit: 5, ttl: 5.minutes, enabled: true)
@error_message = I18n.t('code_is_invalid') @error_message = I18n.t('code_is_invalid')
render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/edit'), status: :unprocessable_content render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/edit'), status: :unprocessable_content

@ -1,7 +0,0 @@
# frozen_string_literal: true
class SettingsController < ApplicationController
skip_authorization_check
def index; end
end

@ -24,7 +24,5 @@ class SubmissionEventsController < ApplicationController
load_and_authorize_resource :submission load_and_authorize_resource :submission
def index def index; end
render :index, layout: 'plain'
end
end end

@ -33,8 +33,6 @@ class SubmissionsController < ApplicationController
def new def new
authorize!(:new, Submission) authorize!(:new, Submission)
render :new, layout: 'plain'
end end
def create def create

@ -8,8 +8,6 @@ class SubmittersController < ApplicationController
if @submitter.preferences['email_message_uuid'].present? if @submitter.preferences['email_message_uuid'].present?
@submitter.account.email_messages.find_by(uuid: @submitter.preferences['email_message_uuid']) @submitter.account.email_messages.find_by(uuid: @submitter.preferences['email_message_uuid'])
end end
render :edit, layout: 'plain'
end end
def update def update

@ -25,9 +25,7 @@ class TemplatesController < ApplicationController
redirect_to root_path redirect_to root_path
end end
def new def new; end
render :new, layout: 'plain'
end
def edit def edit
@template_data = Templates.serialize_for_builder(@template) @template_data = Templates.serialize_for_builder(@template)

@ -13,13 +13,11 @@ class TemplatesFormPreviewController < ApplicationController
@submitter.submission.submitters = @template.submitters.map { |item| Submitter.new(uuid: item['uuid']) } @submitter.submission.submitters = @template.submitters.map { |item| Submitter.new(uuid: item['uuid']) }
Submissions::CreateFromSubmitters.maybe_set_dynamic_documents(@submitter.submission, preview: true)
Submissions.preload_with_pages(@submitter.submission) Submissions.preload_with_pages(@submitter.submission)
@attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments) @attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments)
.preload(:blob).index_by(&:uuid) .preload(:blob).index_by(&:uuid)
@form_configs = Submitters::FormConfigs.call(@submitter, SubmitFormController::CONFIG_KEYS) @form_configs = Submitters::FormConfigs.call(@submitter)
end end
end end

@ -14,9 +14,7 @@ class TemplatesPreferencesController < ApplicationController
completed_notification_email_body] completed_notification_email_body]
}.freeze }.freeze
def show def show; end
render :show, layout: 'plain'
end
def create def create
authorize!(:update, @template) authorize!(:update, @template)

@ -3,9 +3,7 @@
class TemplatesShareLinkController < ApplicationController class TemplatesShareLinkController < ApplicationController
load_and_authorize_resource :template load_and_authorize_resource :template
def show def show; end
render :show, layout: 'plain'
end
def create def create
authorize!(:update, @template) authorize!(:update, @template)

@ -56,9 +56,6 @@ import OpenModal from './elements/open_modal'
import BarChart from './elements/bar_chart' import BarChart from './elements/bar_chart'
import FieldCondition from './elements/field_condition' import FieldCondition from './elements/field_condition'
import ConfirmUpload from './elements/confirm_upload' import ConfirmUpload from './elements/confirm_upload'
import ScrollFade from './elements/scroll_fade'
import OpenModalMobile from './elements/open_modal_mobile'
import HistoryBack from './elements/history_back'
import * as TurboInstantClick from './lib/turbo_instant_click' import * as TurboInstantClick from './lib/turbo_instant_click'
@ -153,14 +150,10 @@ safeRegisterElement('open-modal', OpenModal)
safeRegisterElement('bar-chart', BarChart) safeRegisterElement('bar-chart', BarChart)
safeRegisterElement('field-condition', FieldCondition) safeRegisterElement('field-condition', FieldCondition)
safeRegisterElement('confirm-upload', ConfirmUpload) safeRegisterElement('confirm-upload', ConfirmUpload)
safeRegisterElement('scroll-fade', ScrollFade)
safeRegisterElement('open-modal-mobile', OpenModalMobile)
safeRegisterElement('history-back', HistoryBack)
safeRegisterElement('template-builder', class extends HTMLElement { safeRegisterElement('template-builder', class extends HTMLElement {
connectedCallback () { connectedCallback () {
document.addEventListener('turbo:submit-end', this.onSubmit) document.addEventListener('turbo:submit-end', this.onSubmit)
document.addEventListener('turbo:before-cache', this.onBeforeCache)
this.appElem = document.createElement('div') this.appElem = document.createElement('div')
@ -188,10 +181,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content, authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content,
withCustomFields: true, withCustomFields: true,
withPayment: this.dataset.withPayment === 'true', withPayment: this.dataset.withPayment === 'true',
isStripeConnected: this.dataset.isPaymentConnected === 'true' || this.dataset.isStripeConnected === 'true', isPaymentConnected: this.dataset.isPaymentConnected === 'true',
withStripe: this.dataset.withStripe !== 'false',
withPaypal: this.dataset.withPaypal === 'true',
isPaypalConnected: this.dataset.isPaypalConnected === 'true',
withFormula: this.dataset.withFormula === 'true', withFormula: this.dataset.withFormula === 'true',
withSendButton: this.dataset.withSendButton !== 'false', withSendButton: this.dataset.withSendButton !== 'false',
withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false', withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false',
@ -244,14 +234,8 @@ safeRegisterElement('template-builder', class extends HTMLElement {
} }
} }
onBeforeCache = () => {
this.app?.unmount()
this.appElem?.remove()
}
disconnectedCallback () { disconnectedCallback () {
document.removeEventListener('turbo:submit-end', this.onSubmit) document.removeEventListener('turbo:submit-end', this.onSubmit)
document.removeEventListener('turbo:before-cache', this.onBeforeCache)
this.app?.unmount() this.app?.unmount()
this.appElem?.remove() this.appElem?.remove()
@ -260,8 +244,6 @@ safeRegisterElement('template-builder', class extends HTMLElement {
safeRegisterElement('import-list', class extends HTMLElement { safeRegisterElement('import-list', class extends HTMLElement {
connectedCallback () { connectedCallback () {
document.addEventListener('turbo:before-cache', this.onBeforeCache)
this.appElem = document.createElement('div') this.appElem = document.createElement('div')
this.app = createApp(ImportList, { this.app = createApp(ImportList, {
@ -276,14 +258,7 @@ safeRegisterElement('import-list', class extends HTMLElement {
this.appendChild(this.appElem) this.appendChild(this.appElem)
} }
onBeforeCache = () => {
this.app?.unmount()
this.appElem?.remove()
}
disconnectedCallback () { disconnectedCallback () {
document.removeEventListener('turbo:before-cache', this.onBeforeCache)
this.app?.unmount() this.app?.unmount()
this.appElem?.remove() this.appElem?.remove()
} }

@ -114,13 +114,6 @@ button[disabled] .enabled, button.btn-disabled .enabled {
bottom: auto; bottom: auto;
} }
@media (hover: none), (pointer: coarse) {
.tooltip-no-touch:before,
.tooltip-no-touch:after {
display: none;
}
}
.autocomplete { .autocomplete {
background: white; background: white;
z-index: 1000; z-index: 1000;

@ -1,15 +0,0 @@
export default class extends HTMLElement {
connectedCallback () {
this.addEventListener('click', this.onClick)
}
disconnectedCallback () {
this.removeEventListener('click', this.onClick)
}
onClick = (e) => {
e.preventDefault()
window.history.back()
}
}

@ -1,17 +0,0 @@
export default class extends HTMLElement {
connectedCallback () {
if (this.dataset.disabled === 'true') return
if (!this.isMobile()) return
if (window.innerWidth >= 768) return
this.querySelectorAll('a[data-turbo-frame]').forEach((link) => {
link.dataset.turboFrame = '_top'
})
}
isMobile () {
const isTouchWebkit = 'ontouchstart' in window && navigator.maxTouchPoints > 0 && /AppleWebKit|android/i.test(navigator.userAgent)
return isTouchWebkit || /iPhone|iPad|iPod/i.test(navigator.userAgent)
}
}

@ -1,40 +0,0 @@
export default class extends HTMLElement {
connectedCallback () {
this.addEventListener('scroll', this.updateFade, { passive: true })
window.addEventListener('resize', this.updateFade)
this.updateFade()
}
disconnectedCallback () {
window.removeEventListener('resize', this.updateFade)
}
updateFade = () => {
const maxScroll = this.scrollWidth - this.clientWidth
const fadeGradient = {
right: 'linear-gradient(to right, black calc(100% - 24px), transparent)',
left: 'linear-gradient(to right, transparent, black 24px)',
both: 'linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent)'
}
let state = 'none'
if (maxScroll > 4) {
if (this.scrollLeft <= 4) {
state = 'right'
} else if (this.scrollLeft >= maxScroll - 4) {
state = 'left'
} else {
state = 'both'
}
}
this.applyFade(fadeGradient[state] || '')
}
applyFade (gradient) {
this.style.maskImage = gradient
this.style.webkitMaskImage = gradient
}
}

@ -6,7 +6,7 @@ export default actionable(class extends HTMLElement {
document.addEventListener('keyup', this.onEscKey) document.addEventListener('keyup', this.onEscKey)
document.addEventListener('turbo:before-cache', this.onBeforeCache) document.addEventListener('turbo:before-cache', this.close)
if (this.dataset.closeAfterSubmit !== 'false') { if (this.dataset.closeAfterSubmit !== 'false') {
document.addEventListener('turbo:submit-end', this.onSubmit) document.addEventListener('turbo:submit-end', this.onSubmit)
@ -18,7 +18,7 @@ export default actionable(class extends HTMLElement {
document.removeEventListener('keyup', this.onEscKey) document.removeEventListener('keyup', this.onEscKey)
document.removeEventListener('turbo:submit-end', this.onSubmit) document.removeEventListener('turbo:submit-end', this.onSubmit)
document.removeEventListener('turbo:before-cache', this.onBeforeCache) document.removeEventListener('turbo:before-cache', this.close)
} }
onSubmit = (e) => { onSubmit = (e) => {
@ -33,12 +33,6 @@ export default actionable(class extends HTMLElement {
} }
} }
onBeforeCache = () => {
if (this.closest('turbo-frame')?.src) {
this.remove()
}
}
close = (e) => { close = (e) => {
e?.preventDefault() e?.preventDefault()

@ -35,7 +35,6 @@ safeRegisterElement('submission-form', class extends HTMLElement {
scrollPadding: this.dataset.scrollPadding || '-80px', scrollPadding: this.dataset.scrollPadding || '-80px',
signatureText: this.dataset.signatureText, signatureText: this.dataset.signatureText,
language: this.dataset.language, language: this.dataset.language,
paymentProvider: this.dataset.paymentProvider,
dryRun: this.dataset.dryRun === 'true', dryRun: this.dataset.dryRun === 'true',
expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null, expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null,
withSignatureId: this.dataset.withSignatureId === 'true', withSignatureId: this.dataset.withSignatureId === 'true',

@ -69,8 +69,8 @@
:attachments-index="attachmentsIndex" :attachments-index="attachmentsIndex"
/> />
<FieldArea <FieldArea
v-else-if="item.type === 'formula_area' && (isMathLoaded || item.field.type === 'text')" v-else-if="item.type === 'formula_area' && isMathLoaded"
:model-value="item.field.type === 'text' ? evalTextFormula(item.field) : calculateFormula(item.field)" :model-value="calculateFormula(item.field)"
:is-inline-size="isInlineSize" :is-inline-size="isInlineSize"
:field="item.field" :field="item.field"
:area="item.area" :area="item.area"
@ -289,7 +289,6 @@ export default {
methods: { methods: {
normalizeFormula: FormulaAreas.methods.normalizeFormula, normalizeFormula: FormulaAreas.methods.normalizeFormula,
calculateFormula: FormulaAreas.methods.calculateFormula, calculateFormula: FormulaAreas.methods.calculateFormula,
evalTextFormula: FormulaAreas.methods.evalTextFormula,
scrollInContainer: FieldAreas.methods.scrollInContainer, scrollInContainer: FieldAreas.methods.scrollInContainer,
scrollIntoArea: FieldAreas.methods.scrollIntoArea, scrollIntoArea: FieldAreas.methods.scrollIntoArea,
scrollIntoField: FieldAreas.methods.scrollIntoField, scrollIntoField: FieldAreas.methods.scrollIntoField,

@ -560,7 +560,6 @@
:fields="formulaFields" :fields="formulaFields"
:values="values" :values="values"
:readonly-values="readonlyFieldValues" :readonly-values="readonlyFieldValues"
:provider="paymentProvider"
@attached="attachments.push($event)" @attached="attachments.push($event)"
@focus="scrollIntoField(currentField)" @focus="scrollIntoField(currentField)"
@submit="!isSubmitting && submitStep()" @submit="!isSubmitting && submitStep()"
@ -1001,11 +1000,6 @@ export default {
required: false, required: false,
default: '' default: ''
}, },
paymentProvider: {
type: String,
required: false,
default: ''
},
values: { values: {
type: Object, type: Object,
required: false, required: false,

@ -12,8 +12,8 @@
:to="findPageElementForArea(area)" :to="findPageElementForArea(area)"
> >
<FieldArea <FieldArea
v-if="isMathLoaded || field.type === 'text'" v-if="isMathLoaded"
:model-value="field.type === 'text' ? evalTextFormula(field) : calculateFormula(field)" :model-value="calculateFormula(field)"
:is-inline-size="isInlineSize" :is-inline-size="isInlineSize"
:field="field" :field="field"
:area="area" :area="area"
@ -95,25 +95,6 @@ export default {
}) })
return this.math.evaluate(transformedFormula.toLowerCase()) return this.math.evaluate(transformedFormula.toLowerCase())
},
evalTextFormula (field, depth = 0) {
if (depth > 10) return ''
return field.preferences.formula.replace(/{{(.*?)}}/g, (match, uuid) => {
const formulaField = this.fieldsUuidIndex[uuid]
if (formulaField?.preferences?.formula) {
if (formulaField.type === 'text') {
return this.evalTextFormula(formulaField, depth + 1)
} else if (this.isMathLoaded) {
return this.calculateFormula(formulaField)
}
}
const value = this.readonlyValues[uuid] ?? this.values[uuid]
return Array.isArray(value) ? value.join(', ') : (value ?? '')
})
} }
} }
} }

@ -107,7 +107,6 @@ const en = {
email_has_been_sent: 'Email has been sent', email_has_been_sent: 'Email has been sent',
processing: 'Processing', processing: 'Processing',
pay_with_stripe: 'Pay with Stripe', pay_with_stripe: 'Pay with Stripe',
pay_with_paypal: 'Pay with PayPal',
reupload: 'Reupload', reupload: 'Reupload',
upload: 'Upload', upload: 'Upload',
files: 'Files', files: 'Files',
@ -226,7 +225,6 @@ const es = {
email_has_been_sent: 'El correo electrónico ha sido enviado', email_has_been_sent: 'El correo electrónico ha sido enviado',
processing: 'Procesando', processing: 'Procesando',
pay_with_stripe: 'Pagar con Stripe', pay_with_stripe: 'Pagar con Stripe',
pay_with_paypal: 'Pagar con PayPal',
reupload: 'Volver a subir', reupload: 'Volver a subir',
upload: 'Subir', upload: 'Subir',
files: 'Archivos', files: 'Archivos',
@ -345,7 +343,6 @@ const it = {
email_has_been_sent: "L'email è stata inviata", email_has_been_sent: "L'email è stata inviata",
processing: 'Elaborazione', processing: 'Elaborazione',
pay_with_stripe: 'Paga con Stripe', pay_with_stripe: 'Paga con Stripe',
pay_with_paypal: 'Paga con PayPal',
reupload: 'Ricarica', reupload: 'Ricarica',
upload: 'Carica', upload: 'Carica',
files: 'File', files: 'File',
@ -464,7 +461,6 @@ const de = {
email_has_been_sent: 'E-Mail wurde gesendet', email_has_been_sent: 'E-Mail wurde gesendet',
processing: 'Verarbeitung', processing: 'Verarbeitung',
pay_with_stripe: 'Mit Stripe bezahlen', pay_with_stripe: 'Mit Stripe bezahlen',
pay_with_paypal: 'Mit PayPal bezahlen',
reupload: 'Erneut hochladen', reupload: 'Erneut hochladen',
upload: 'Hochladen', upload: 'Hochladen',
files: 'Dateien', files: 'Dateien',
@ -583,7 +579,6 @@ const fr = {
email_has_been_sent: "L'e-mail a été envoyé", email_has_been_sent: "L'e-mail a été envoyé",
processing: 'Traitement en cours', processing: 'Traitement en cours',
pay_with_stripe: 'Payer avec Stripe', pay_with_stripe: 'Payer avec Stripe',
pay_with_paypal: 'Payer avec PayPal',
reupload: 'Retéléverser', reupload: 'Retéléverser',
upload: 'Téléverser', upload: 'Téléverser',
files: 'Fichiers', files: 'Fichiers',
@ -702,7 +697,6 @@ const pl = {
email_has_been_sent: 'E-mail został wysłany', email_has_been_sent: 'E-mail został wysłany',
processing: 'Przetwarzanie', processing: 'Przetwarzanie',
pay_with_stripe: 'Zapłać za pomocą Stripe', pay_with_stripe: 'Zapłać za pomocą Stripe',
pay_with_paypal: 'Zapłać za pomocą PayPal',
reupload: 'Ponowne przesłanie', reupload: 'Ponowne przesłanie',
upload: 'Przesyłanie', upload: 'Przesyłanie',
files: 'Pliki', files: 'Pliki',
@ -821,7 +815,6 @@ const uk = {
email_has_been_sent: 'Електронний лист був відправлений', email_has_been_sent: 'Електронний лист був відправлений',
processing: 'Обробка', processing: 'Обробка',
pay_with_stripe: 'Сплатити за допомогою Stripe', pay_with_stripe: 'Сплатити за допомогою Stripe',
pay_with_paypal: 'Сплатити за допомогою PayPal',
reupload: 'Перезавантажити', reupload: 'Перезавантажити',
upload: 'Завантажити', upload: 'Завантажити',
files: 'Файли', files: 'Файли',
@ -940,7 +933,6 @@ const cs = {
email_has_been_sent: 'E-mail byl odeslán', email_has_been_sent: 'E-mail byl odeslán',
processing: 'Zpracování', processing: 'Zpracování',
pay_with_stripe: 'Zaplatit přes Stripe', pay_with_stripe: 'Zaplatit přes Stripe',
pay_with_paypal: 'Zaplatit přes PayPal',
reupload: 'Znovu nahrát', reupload: 'Znovu nahrát',
upload: 'Nahrát', upload: 'Nahrát',
files: 'Soubory', files: 'Soubory',
@ -1059,7 +1051,6 @@ const pt = {
email_has_been_sent: 'Email enviado', email_has_been_sent: 'Email enviado',
processing: 'Processamento', processing: 'Processamento',
pay_with_stripe: 'Pagar com Stripe', pay_with_stripe: 'Pagar com Stripe',
pay_with_paypal: 'Pagar com PayPal',
reupload: 'Recarregar', reupload: 'Recarregar',
upload: 'Carregar', upload: 'Carregar',
files: 'Arquivos', files: 'Arquivos',
@ -1178,7 +1169,6 @@ const he = {
email_has_been_sent: 'האימייל נשלח', email_has_been_sent: 'האימייל נשלח',
processing: 'מעבד', processing: 'מעבד',
pay_with_stripe: 'שלם עם סטרייפ', pay_with_stripe: 'שלם עם סטרייפ',
pay_with_paypal: 'שלם עם פייפאל',
reupload: 'העלה שוב', reupload: 'העלה שוב',
upload: 'העלאה', upload: 'העלאה',
files: 'קבצים', files: 'קבצים',
@ -1297,7 +1287,6 @@ const nl = {
email_has_been_sent: 'E-mail is verzonden', email_has_been_sent: 'E-mail is verzonden',
processing: 'Verwerken', processing: 'Verwerken',
pay_with_stripe: 'Betalen met Stripe', pay_with_stripe: 'Betalen met Stripe',
pay_with_paypal: 'Betalen met PayPal',
reupload: 'Opnieuw uploaden', reupload: 'Opnieuw uploaden',
upload: 'Uploaden', upload: 'Uploaden',
files: 'Bestanden', files: 'Bestanden',
@ -1416,7 +1405,6 @@ const ar = {
email_has_been_sent: 'تم إرسال البريد الإلكتروني', email_has_been_sent: 'تم إرسال البريد الإلكتروني',
processing: 'جارٍ المعالجة', processing: 'جارٍ المعالجة',
pay_with_stripe: 'الدفع بواسطة Stripe', pay_with_stripe: 'الدفع بواسطة Stripe',
pay_with_paypal: 'الدفع بواسطة PayPal',
reupload: 'إعادة التحميل', reupload: 'إعادة التحميل',
upload: 'تحميل', upload: 'تحميل',
files: 'الملفات', files: 'الملفات',
@ -1535,7 +1523,6 @@ const ko = {
email_has_been_sent: '이메일이 전송되었습니다', email_has_been_sent: '이메일이 전송되었습니다',
processing: '처리 중', processing: '처리 중',
pay_with_stripe: '스트라이프로 결제', pay_with_stripe: '스트라이프로 결제',
pay_with_paypal: 'PayPal로 결제',
reupload: '다시 업로드', reupload: '다시 업로드',
upload: '업로드', upload: '업로드',
files: '파일', files: '파일',
@ -1654,7 +1641,6 @@ const ja = {
email_has_been_sent: 'メールが送信されました', email_has_been_sent: 'メールが送信されました',
processing: '処理中', processing: '処理中',
pay_with_stripe: 'Stripeで支払う', pay_with_stripe: 'Stripeで支払う',
pay_with_paypal: 'PayPalで支払う',
reupload: '再アップロード', reupload: '再アップロード',
upload: 'アップロード', upload: 'アップロード',
files: 'ファイル', files: 'ファイル',

@ -1,6 +1,6 @@
<template> <template>
<label <label
v-if="!modelValue && !isProcessing" v-if="!modelValue && !sessionId"
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label" class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
> >
<MarkdownContent <MarkdownContent
@ -25,14 +25,14 @@
class="hidden" class="hidden"
> >
<div <div
v-if="modelValue && !isProcessing" v-if="modelValue && !sessionId"
class=" text-2xl mb-2" class=" text-2xl mb-2"
> >
{{ t('already_paid') }} {{ t('already_paid') }}
</div> </div>
<div v-else> <div v-else>
<button <button
v-if="isProcessing" v-if="sessionId"
type="button" type="button"
disabled disabled
class="base-button w-full modal-save-button" class="base-button w-full modal-save-button"
@ -49,8 +49,8 @@
v-else v-else
:id="field.uuid" :id="field.uuid"
type="button" type="button"
class="btn text-white text-lg w-full" class="btn bg-[#7B73FF] text-white hover:bg-[#0A2540] text-lg w-full"
:class="[isPaypal ? 'bg-[#003087] hover:bg-[#012169]' : 'bg-[#7B73FF] hover:bg-[#0A2540]', { disabled: isCreatingCheckout }]" :class="{ disabled: isCreatingCheckout }"
:disabled="isCreatingCheckout" :disabled="isCreatingCheckout"
@click.prevent="postCheckout" @click.prevent="postCheckout"
> >
@ -59,16 +59,12 @@
width="22" width="22"
class="animate-spin" class="animate-spin"
/> />
<IconBrandPaypal
v-else-if="isPaypal"
width="22"
/>
<IconBrandStripe <IconBrandStripe
v-else v-else
width="22" width="22"
/> />
<span> <span>
{{ isPaypal ? t('pay_with_paypal') : t('pay_with_stripe') }} {{ t('pay_with_stripe') }}
</span> </span>
</button> </button>
</div> </div>
@ -76,14 +72,13 @@
</template> </template>
<script> <script>
import { IconBrandStripe, IconBrandPaypal, IconInnerShadowTop, IconLoader } from '@tabler/icons-vue' import { IconBrandStripe, IconInnerShadowTop, IconLoader } from '@tabler/icons-vue'
import MarkdownContent from './markdown_content' import MarkdownContent from './markdown_content'
export default { export default {
name: 'PaymentStep', name: 'PaymentStep',
components: { components: {
IconBrandStripe, IconBrandStripe,
IconBrandPaypal,
MarkdownContent, MarkdownContent,
IconInnerShadowTop, IconInnerShadowTop,
IconLoader IconLoader
@ -116,18 +111,12 @@ export default {
submitterSlug: { submitterSlug: {
type: String, type: String,
required: true required: true
},
provider: {
type: String,
required: false,
default: ''
} }
}, },
emits: ['focus', 'submit', 'update:model-value', 'attached'], emits: ['focus', 'submit', 'update:model-value', 'attached'],
data () { data () {
return { return {
isCreatingCheckout: false, isCreatingCheckout: false,
isProcessing: false,
isMathLoaded: false isMathLoaded: false
} }
}, },
@ -142,14 +131,8 @@ export default {
queryParams () { queryParams () {
return new URLSearchParams(window.location.search) return new URLSearchParams(window.location.search)
}, },
isPaypal () {
return this.provider === 'paypal'
},
paymentEndpoint () {
return this.isPaypal ? '/api/paypal_payments' : '/api/stripe_payments'
},
sessionId () { sessionId () {
return this.queryParams.get('stripe_session_id') || this.queryParams.get('paypal_order_id') || (this.isPaypal && this.queryParams.get('token')) return this.queryParams.get('stripe_session_id')
}, },
defaultName () { defaultName () {
if (this.field.preferences?.price_id || this.field.preferences?.payment_link_id) { if (this.field.preferences?.price_id || this.field.preferences?.payment_link_id) {
@ -176,8 +159,6 @@ export default {
}, },
async mounted () { async mounted () {
if (this.sessionId) { if (this.sessionId) {
this.isProcessing = true
this.$emit('submit') this.$emit('submit')
} }
@ -194,21 +175,6 @@ export default {
} }
}, },
methods: { methods: {
normalizedPaymentUrl () {
const url = new URL(window.location.href)
const keys = ['stripe_session_id', 'paypal_order_id', 'field_uuid']
if (this.isPaypal) {
keys.push('token', 'PayerID')
}
keys.forEach((key) => url.searchParams.delete(key))
return url.toString()
},
normalizedPaymentParams () {
window.history.replaceState({}, document.title, this.normalizedPaymentUrl())
},
calculateFormula () { calculateFormula () {
const transformedFormula = this.normalizeFormula(this.field.preferences.formula).replace(/{{(.*?)}}/g, (match, uuid) => { const transformedFormula = this.normalizeFormula(this.field.preferences.formula).replace(/{{(.*?)}}/g, (match, uuid) => {
return this.readonlyValues[uuid] || this.values[uuid] || 0.0 return this.readonlyValues[uuid] || this.values[uuid] || 0.0
@ -229,7 +195,7 @@ export default {
}, },
async submit () { async submit () {
if (this.sessionId) { if (this.sessionId) {
return fetch(this.baseUrl + this.paymentEndpoint + '/' + this.sessionId, { return fetch(this.baseUrl + '/api/stripe_payments/' + this.sessionId, {
method: 'PUT', method: 'PUT',
body: JSON.stringify({ body: JSON.stringify({
submitter_slug: this.submitterSlug submitter_slug: this.submitterSlug
@ -237,22 +203,16 @@ export default {
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}).then(async (resp) => { }).then(async (resp) => {
if (resp.status === 422 || resp.status === 500) { if (resp.status === 422 || resp.status === 500) {
this.isProcessing = false
const data = await resp.json() const data = await resp.json()
if (data.error) { alert(data.error || 'Unexpected error')
alert(data.error)
}
return Promise.reject(new Error(data.error)) return Promise.reject(new Error(data.error))
} }
const attachment = await resp.json() const attachment = await resp.json()
this.isProcessing = false window.history.replaceState({}, document.title, window.location.pathname)
this.normalizedPaymentParams()
this.$emit('update:model-value', attachment.uuid) this.$emit('update:model-value', attachment.uuid)
this.$emit('attached', attachment) this.$emit('attached', attachment)
@ -266,13 +226,13 @@ export default {
postCheckout ({ checkStatus } = {}) { postCheckout ({ checkStatus } = {}) {
this.isCreatingCheckout = true this.isCreatingCheckout = true
fetch(this.baseUrl + this.paymentEndpoint, { fetch(this.baseUrl + '/api/stripe_payments', {
method: 'POST', method: 'POST',
body: JSON.stringify({ body: JSON.stringify({
submitter_slug: this.submitterSlug, submitter_slug: this.submitterSlug,
field_uuid: this.field.uuid, field_uuid: this.field.uuid,
check_status: checkStatus, check_status: checkStatus,
referer: this.normalizedPaymentUrl() referer: document.location.href
}), }),
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}).then(async (resp) => { }).then(async (resp) => {

@ -39,7 +39,7 @@
required required
maxlength="6" maxlength="6"
autofocus autofocus
inputmode="numeric" inputmode="decimal"
@input="onInputCode" @input="onInputCode"
> >
<div class="flex justify-between mt-2 -mb-2 md:-mb-4"> <div class="flex justify-between mt-2 -mb-2 md:-mb-4">

@ -164,7 +164,7 @@
v-if="withSendButton" v-if="withSendButton"
id="send_button" id="send_button"
:href="`/templates/${template.id}/submissions/new?with_link=true`" :href="`/templates/${template.id}/submissions/new?with_link=true`"
:data-turbo-frame="isMobile && isBreakpointLg ? '_top' : 'modal'" data-turbo-frame="modal"
class="white-button md:!px-6" class="white-button md:!px-6"
@click="maybeShowErrorTemplateAlert" @click="maybeShowErrorTemplateAlert"
> >
@ -202,7 +202,7 @@
</button> </button>
<div <div
class="dropdown dropdown-end" class="dropdown dropdown-end"
:class="{ 'dropdown-open': isDownloading || isPreviewLoading }" :class="{ 'dropdown-open': isDownloading }"
> >
<label <label
tabindex="0" tabindex="0"
@ -221,23 +221,15 @@
:href="`/templates/${template.id}/form`" :href="`/templates/${template.id}/form`"
data-turbo="false" data-turbo="false"
class="flex items-center justify-center space-x-2" class="flex items-center justify-center space-x-2"
@click.exact="isPreviewLoading = true"
> >
<IconInnerShadowTop <IconEye class="w-6 h-6 flex-shrink-0" />
v-if="isPreviewLoading"
class="animate-spin w-6 h-6 flex-shrink-0"
/>
<IconEye
v-else
class="w-6 h-6 flex-shrink-0"
/>
<span class="whitespace-nowrap">{{ t('save_and_preview') }}</span> <span class="whitespace-nowrap">{{ t('save_and_preview') }}</span>
</a> </a>
</li> </li>
<li> <li>
<a <a
:href="`/templates/${template.id}/preferences`" :href="`/templates/${template.id}/preferences`"
:data-turbo-frame="isMobile && isBreakpointLg ? '_top' : 'modal'" data-turbo-frame="modal"
class="flex space-x-2" class="flex space-x-2"
@click="closeDropdown" @click="closeDropdown"
> >
@ -289,10 +281,6 @@
:href="`/templates/${template.id}`" :href="`/templates/${template.id}`"
class="base-button" class="base-button"
> >
<IconX
width="20"
class="md:hidden stroke-2"
/>
<span class="hidden md:inline"> <span class="hidden md:inline">
{{ t('back') }} {{ t('back') }}
</span> </span>
@ -791,10 +779,7 @@ export default {
withVerification: this.withVerification, withVerification: this.withVerification,
withKba: this.withKba, withKba: this.withKba,
withPayment: this.withPayment, withPayment: this.withPayment,
isStripeConnected: this.isStripeConnected, isPaymentConnected: this.isPaymentConnected,
withStripe: this.withStripe,
withPaypal: this.withPaypal,
isPaypalConnected: this.isPaypalConnected,
withFormula: this.withFormula, withFormula: this.withFormula,
withConditions: this.withConditions, withConditions: this.withConditions,
withCustomFields: this.withCustomFields, withCustomFields: this.withCustomFields,
@ -1064,22 +1049,7 @@ export default {
required: false, required: false,
default: false default: false
}, },
isStripeConnected: { isPaymentConnected: {
type: Boolean,
required: false,
default: false
},
withStripe: {
type: Boolean,
required: false,
default: true
},
withPaypal: {
type: Boolean,
required: false,
default: false
},
isPaypalConnected: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false default: false
@ -1130,7 +1100,6 @@ export default {
documentRefs: [], documentRefs: [],
isBreakpointLg: false, isBreakpointLg: false,
isDownloading: false, isDownloading: false,
isPreviewLoading: false,
isLoadingBlankPage: false, isLoadingBlankPage: false,
isSaving: false, isSaving: false,
isDetectingPageFields: false, isDetectingPageFields: false,
@ -1272,7 +1241,7 @@ export default {
return formulaFields.reduce((acc, f) => { return formulaFields.reduce((acc, f) => {
if (this.conditionalFieldIndex[f.uuid] !== false) { if (this.conditionalFieldIndex[f.uuid] !== false) {
acc[f.uuid] = f.type === 'text' ? this.evalTextFormula(f) : this.calculateFormula(f) acc[f.uuid] = this.calculateFormula(f)
} }
return acc return acc
@ -1394,10 +1363,6 @@ export default {
if (document.location.search?.includes('stripe_connect_success')) { if (document.location.search?.includes('stripe_connect_success')) {
document.querySelector('form[action="/auth/stripe_connect"]')?.closest('.dropdown')?.querySelector('label')?.focus() document.querySelector('form[action="/auth/stripe_connect"]')?.closest('.dropdown')?.querySelector('label')?.focus()
} }
if (document.location.search?.includes('paypal_connect_success')) {
document.querySelector('a[href^="/auth/paypal_connect"]')?.closest('.dropdown')?.querySelector('label')?.focus()
}
}) })
this.template.schema.forEach((item) => { this.template.schema.forEach((item) => {
@ -1539,25 +1504,6 @@ export default {
return this.math.evaluate(transformedFormula.toLowerCase()) return this.math.evaluate(transformedFormula.toLowerCase())
}, },
evalTextFormula (field, depth = 0) {
if (depth > 10) return ''
return field.preferences.formula.replace(/{{(.*?)}}/g, (match, uuid) => {
const formulaField = this.fieldsUuidIndex[uuid]
if (formulaField?.preferences?.formula) {
if (formulaField.type === 'text') {
return this.evalTextFormula(formulaField, depth + 1)
} else if (this.isMathLoaded) {
return this.calculateFormula(formulaField)
}
}
const value = formulaField?.default_value
return Array.isArray(value) ? value.join(', ') : (value ?? '')
})
},
hasFormulaDependencyValue (field) { hasFormulaDependencyValue (field) {
const normalized = this.normalizeFormula(field.preferences.formula) const normalized = this.normalizeFormula(field.preferences.formula)

@ -494,7 +494,6 @@ import FieldType from './field_type.vue'
import FieldSettings from './field_settings.vue' import FieldSettings from './field_settings.vue'
import ContextSubmenu from './field_context_submenu.vue' import ContextSubmenu from './field_context_submenu.vue'
import ContextModal from './field_context_modal.vue' import ContextModal from './field_context_modal.vue'
import { stripeConnectedRef } from './payment_settings'
export default { export default {
name: 'FieldContextMenu', name: 'FieldContextMenu',
@ -611,7 +610,7 @@ export default {
return this.withCondition && !['stamp', 'heading'].includes(this.field.type) return this.withCondition && !['stamp', 'heading'].includes(this.field.type)
}, },
showFormula () { showFormula () {
return this.field.type === 'number' || this.field.type === 'payment' || !!this.field.preferences?.formula return this.field.type === 'number' || this.field.type === 'payment'
}, },
showRequired () { showRequired () {
return this.withRequired && !['phone', 'stamp', 'verification', 'strikethrough', 'heading'].includes(this.field.type) return this.withRequired && !['phone', 'stamp', 'verification', 'strikethrough', 'heading'].includes(this.field.type)
@ -717,18 +716,12 @@ export default {
currentCurrency () { currentCurrency () {
return this.field.preferences?.currency || 'USD' return this.field.preferences?.currency || 'USD'
}, },
isStripeConnected: () => stripeConnectedRef.value,
priceTypeOptions () { priceTypeOptions () {
const options = [ return [
{ value: 'one_off', label: this.t('fixed') }, { value: 'one_off', label: this.t('fixed') },
{ value: 'formula', label: this.t('formula') } { value: 'formula', label: this.t('formula') },
{ value: 'payment_link', label: this.t('payment_link') }
] ]
if (this.isStripeConnected) {
options.push({ value: 'payment_link', label: this.t('payment_link') })
}
return options
}, },
currentPriceType () { currentPriceType () {
if (this.field.preferences?.formula) { if (this.field.preferences?.formula) {

@ -500,7 +500,7 @@
</label> </label>
</li> </li>
<li <li
v-if="field.type == 'number' || field.preferences?.formula" v-if="field.type == 'number'"
class="field-settings-formula" class="field-settings-formula"
> >
<label <label

@ -61,10 +61,7 @@
@input="resizeTextarea" @input="resizeTextarea"
/> />
</div> </div>
<div <div class="mb-3 mt-1">
v-if="field.type !== 'text'"
class="mb-3 mt-1"
>
<div <div
target="blank" target="blank"
class="text-sm mb-2 inline space-x-2 font-mono" class="text-sm mb-2 inline space-x-2 font-mono"
@ -164,9 +161,7 @@ export default {
computed: { computed: {
fields () { fields () {
return this.template.fields.reduce((acc, f) => { return this.template.fields.reduce((acc, f) => {
const isAllowed = this.field.type === 'text' ? this.isTextField(f) : this.isNumberField(f) if (f !== this.field && this.isNumberField(f) && (!f.preferences?.formula || !f.preferences.formula.includes(this.field.uuid))) {
if (f !== this.field && isAllowed && (!f.preferences?.formula || !f.preferences.formula.includes(this.field.uuid))) {
acc.push(f) acc.push(f)
} }
@ -184,9 +179,6 @@ export default {
isNumberField (field) { isNumberField (field) {
return field.type === 'number' || (['radio', 'select'].includes(field.type) && field.options?.every((o) => String(o.value).match(/^[\d.-]+$/))) return field.type === 'number' || (['radio', 'select'].includes(field.type) && field.options?.every((o) => String(o.value).match(/^[\d.-]+$/)))
}, },
isTextField (field) {
return ['text', 'number', 'select', 'radio', 'cells', 'phone'].includes(field.type)
},
humanizeFormula (text) { humanizeFormula (text) {
return text.replace(/{{(.*?)}}/g, (match, uuid) => { return text.replace(/{{(.*?)}}/g, (match, uuid) => {
const foundField = this.template.fields.find((f) => f.uuid === uuid) const foundField = this.template.fields.find((f) => f.uuid === uuid)

@ -1,7 +1,7 @@
<template> <template>
<span <span
class="dropdown dropdown-end field-settings-dropdown" class="dropdown dropdown-end field-settings-dropdown"
:class="{ 'dropdown-open': withForceOpen && !field.preferences?.price && !field.preferences?.formula && !field.preferences?.price_id && !field.preferences?.payment_link_id && !isLoading }" :class="{ 'dropdown-open': withForceOpen && ((!field.preferences?.price && !field.preferences?.formula && !field.preferences?.price_id && !field.preferences?.payment_link_id) || !isConnected) && !isLoading }"
> >
<label <label
tabindex="0" tabindex="0"
@ -21,7 +21,7 @@
@click="closeDropdown" @click="closeDropdown"
> >
<div <div
v-if="isPaypal || (!('price_id' in field.preferences) && !('payment_link_id' in field.preferences))" v-if="!('price_id' in field.preferences) && !('payment_link_id' in field.preferences)"
class="field-settings-currency py-1.5 px-1 relative" class="field-settings-currency py-1.5 px-1 relative"
@click.stop @click.stop
> >
@ -52,14 +52,14 @@
@click.stop @click.stop
> >
<input <input
v-if="isStripe && 'payment_link_id' in field.preferences" v-if="'payment_link_id' in field.preferences"
v-model="field.preferences.payment_link_id" v-model="field.preferences.payment_link_id"
placeholder="plink_XXXXX" placeholder="plink_XXXXX"
class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0" class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0"
@blur="save" @blur="save"
> >
<input <input
v-else-if="isStripe && 'price_id' in field.preferences" v-else-if="'price_id' in field.preferences"
v-model="field.preferences.price_id" v-model="field.preferences.price_id"
placeholder="Price ID: price_XXXXX" placeholder="Price ID: price_XXXXX"
class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0" class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0"
@ -89,10 +89,7 @@
> >
{{ 'payment_link_id' in field.preferences ? t('payment_link') : t('price') }} {{ 'payment_link_id' in field.preferences ? t('payment_link') : t('price') }}
</label> </label>
<div <div class="flex items-center justify-center">
v-if="isStripe && stripeConnected"
class="flex items-center justify-center"
>
<a <a
href="#" href="#"
class="hover:underline" class="hover:underline"
@ -123,22 +120,21 @@
</div> </div>
</div> </div>
<div <div
v-if="!stripeConnected && !paypalConnected" v-if="!isConnected || isOauthSuccess"
class="flex flex-col gap-1 py-1.5 px-1" class="field-settings-stripe-connect py-1.5 px-1 relative"
@click.stop @click.stop
> >
<a <div
v-if="withPaypal" v-if="isConnected && isOauthSuccess"
:href="paypalConnectUrl" class="text-sm text-center"
target="_blank"
data-turbo="false"
class="btn bg-[#003087] hover:bg-[#012169] btn-sm text-white w-full"
> >
<IconBrandPaypal class="w-4 h-4 inline" /> <IconCircleCheck
<span>Connect PayPal</span> class="inline text-green-600 w-4 h-4"
</a> />
Stripe Connected
</div>
<form <form
v-if="withStripe" v-if="!isConnected"
data-turbo="false" data-turbo="false"
action="/auth/stripe_connect" action="/auth/stripe_connect"
accept-charset="UTF-8" accept-charset="UTF-8"
@ -174,25 +170,33 @@
:disabled="isLoading" :disabled="isLoading"
class="btn bg-[#7B73FF] hover:bg-[#0A2540] btn-sm text-white w-full" class="btn bg-[#7B73FF] hover:bg-[#0A2540] btn-sm text-white w-full"
> >
<IconInnerShadowTop <span
v-if="isLoading" v-if="isLoading"
class="flex items-center space-x-1"
>
<IconInnerShadowTop
class="w-4 h-4 animate-spin inline" class="w-4 h-4 animate-spin inline"
/> />
<IconBrandStripe <span>
Connect Stripe
</span>
</span>
<span
v-else v-else
class="flex items-center space-x-1"
>
<IconBrandStripe
class="w-4 h-4 inline" class="w-4 h-4 inline"
/> />
<span>Connect Stripe</span> <span>
Connect Stripe
</span>
</span>
</button> </button>
</form> </form>
</div>
<div
v-if="isStripe && !stripeConnected"
class="px-1 pb-1.5"
@click.stop
>
<a <a
class="block link text-center text-xs" v-if="!isConnected"
class="block link text-center mt-1"
href="https://www.docuseal.com/blog/accept-payments-and-request-signatures-with-ease" href="https://www.docuseal.com/blog/accept-payments-and-request-signatures-with-ease"
target="_blank" target="_blank"
data-turbo="false" data-turbo="false"
@ -268,25 +272,24 @@
</template> </template>
<script> <script>
import { IconMathFunction, IconSettings, IconInfoCircle, IconBrandStripe, IconBrandPaypal, IconInnerShadowTop, IconRouteAltLeft, IconForms } from '@tabler/icons-vue' import { IconMathFunction, IconSettings, IconCircleCheck, IconInfoCircle, IconBrandStripe, IconInnerShadowTop, IconRouteAltLeft, IconForms } from '@tabler/icons-vue'
import { ref } from 'vue' import { ref } from 'vue'
export const stripeConnectedRef = ref(false) const isConnected = ref(false)
export const paypalConnectedRef = ref(false)
export default { export default {
name: 'PaymentSettings', name: 'PaymentSettings',
components: { components: {
IconSettings, IconSettings,
IconCircleCheck,
IconRouteAltLeft, IconRouteAltLeft,
IconInfoCircle, IconInfoCircle,
IconForms, IconForms,
IconMathFunction, IconMathFunction,
IconInnerShadowTop, IconInnerShadowTop,
IconBrandStripe, IconBrandStripe
IconBrandPaypal
}, },
inject: ['backgroundColor', 'save', 'currencies', 't', 'isStripeConnected', 'isPaypalConnected', 'withStripe', 'withPaypal'], inject: ['backgroundColor', 'save', 'currencies', 't', 'isPaymentConnected'],
props: { props: {
field: { field: {
type: Object, type: Object,
@ -315,27 +318,13 @@ export default {
} }
}, },
computed: { computed: {
stripeConnected: () => stripeConnectedRef.value, isConnected: () => isConnected.value,
paypalConnected: () => paypalConnectedRef.value, isOauthSuccess () {
provider () { return document.location.search?.includes('stripe_connect_success')
if (this.paypalConnected && !this.stripeConnected) return 'paypal'
if (this.stripeConnected && !this.paypalConnected) return 'stripe'
if (this.withPaypal && !this.withStripe) return 'paypal'
return 'stripe'
},
isStripe () {
return this.provider === 'stripe'
},
isPaypal () {
return this.provider === 'paypal'
}, },
redirectUri () { redirectUri () {
return document.location.origin + '/auth/stripe_connect/callback' return document.location.origin + '/auth/stripe_connect/callback'
}, },
paypalConnectUrl () {
return `/auth/paypal_connect?redir=${encodeURIComponent(document.location.href)}`
},
defaultCurrencies () { defaultCurrencies () {
return ['USD', 'EUR', 'GBP', 'CAD', 'AUD'] return ['USD', 'EUR', 'GBP', 'CAD', 'AUD']
}, },
@ -374,53 +363,26 @@ export default {
mounted () { mounted () {
this.field.preferences.currency ||= this.defaultCurrency this.field.preferences.currency ||= this.defaultCurrency
stripeConnectedRef.value ||= this.isStripeConnected isConnected.value ||= this.isPaymentConnected
paypalConnectedRef.value ||= this.isPaypalConnected
if (this.withStripe && !this.stripeConnected && !this.paypalConnected) { if (!this.isConnected) {
this.checkStripeStatus() this.checkStatus()
} }
if (this.withPaypal && !this.paypalConnected && !this.stripeConnected) {
this.checkPaypalStatus()
window.addEventListener('focus', this.onWindowFocus)
}
},
beforeUnmount () {
window.removeEventListener('focus', this.onWindowFocus)
}, },
methods: { methods: {
onWindowFocus () { checkStatus () {
if (this.withPaypal && !this.paypalConnected) {
this.checkPaypalStatus()
}
},
checkStripeStatus () {
this.isLoading = true this.isLoading = true
fetch('/api/stripe_connect').then(async (resp) => { fetch('/api/stripe_connect').then(async (resp) => {
const { status } = await resp.json() const { status } = await resp.json()
if (status === 'connected') { if (status === 'connected') {
stripeConnectedRef.value = true isConnected.value = true
window.removeEventListener('focus', this.onWindowFocus)
} }
}).finally(() => { }).finally(() => {
this.isLoading = false this.isLoading = false
}) })
}, },
checkPaypalStatus () {
fetch('/api/paypal_connect').then(async (resp) => {
const { status } = await resp.json()
if (status === 'connected') {
paypalConnectedRef.value = true
window.removeEventListener('focus', this.onWindowFocus)
}
})
},
closeDropdown () { closeDropdown () {
document.activeElement.blur() document.activeElement.blur()
} }

@ -3,9 +3,6 @@
class ProcessSubmitterCompletionJob class ProcessSubmitterCompletionJob
include Sidekiq::Job include Sidekiq::Job
BCC_LIMIT = 14
TooManyBcc = Class.new(StandardError)
def perform(params = {}) def perform(params = {})
submitter = Submitter.find(params['submitter_id']) submitter = Submitter.find(params['submitter_id'])
submission = submitter.submission submission = submitter.submission
@ -136,24 +133,16 @@ class ProcessSubmitterCompletionJob
true true
end end
enqueue_bcc_completed_emails(submitter, user, is_sent_to_user) build_bcc_addresses(submission).each do |to|
end
maybe_enqueue_copy_emails(submitter)
end
def enqueue_bcc_completed_emails(submitter, user, is_sent_to_user)
bcc_addresses = build_bcc_addresses(submitter.submission)
raise TooManyBcc, submitter.account_id if Docuseal.multitenant? && bcc_addresses.size > BCC_LIMIT
bcc_addresses.each do |to|
next if is_sent_to_user && to == user.email next if is_sent_to_user && to == user.email
SubmitterMailer.completed_email(submitter, user, to:).deliver_later! SubmitterMailer.completed_email(submitter, user, to:).deliver_later!
end end
end end
maybe_enqueue_copy_emails(submitter)
end
def maybe_enqueue_copy_emails(submitter) def maybe_enqueue_copy_emails(submitter)
return if submitter.template&.preferences&.dig('documents_copy_email_enabled') == false return if submitter.template&.preferences&.dig('documents_copy_email_enabled') == false

@ -1,7 +1,7 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-4"> <h1 class="text-4xl font-bold mb-4">
<%= t('account') %> <%= t('account') %>
</h1> </h1>
<%= form_for '', url: settings_account_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for '', url: settings_account_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
@ -53,8 +53,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('force_2fa_with_authenticator_app') %></span> <span class="text-left"><%= t('force_2fa_with_authenticator_app') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('require_two_factor_authentication_2fa_with_an_authenticator_app_e_g_google_authenticator_authy_all_users_signing_documents_must_pass_the_second_factor_verification_using_a_secure_code_in_addition_to_their_password') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('require_two_factor_authentication_2fa_with_an_authenticator_app_e_g_google_authenticator_authy_all_users_signing_documents_must_pass_the_second_factor_verification_using_a_secure_code_in_addition_to_their_password') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -70,8 +70,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('add_signature_id_to_the_documents') %></span> <span class="text-left"><%= t('add_signature_id_to_the_documents') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('add_a_unique_signature_id_and_timestamp_to_each_signature_for_audit_and_traceability_purposes_along_with_the_timestamp_part_of_docuseals_21_cfr_part_11_compliance_settings') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('add_a_unique_signature_id_and_timestamp_to_each_signature_for_audit_and_traceability_purposes_along_with_the_timestamp_part_of_docuseals_21_cfr_part_11_compliance_settings') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -87,8 +87,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('require_signing_reason') %></span> <span class="text-left"><%= t('require_signing_reason') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('require_signer_to_provide_a_reason_for_signing_before_completing_their_signature_e_g_approvals_certifications_part_of_docuseals_21_cfr_part_11_compliance_settings') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('require_signer_to_provide_a_reason_for_signing_before_completing_their_signature_e_g_approvals_certifications_part_of_docuseals_21_cfr_part_11_compliance_settings') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -104,8 +104,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('allow_typed_text_signatures') %></span> <span class="text-left"><%= t('allow_typed_text_signatures') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('allow_signers_to_create_signatures_by_typing_their_name_instead_of_drawing_or_uploading_one') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('allow_signers_to_create_signatures_by_typing_their_name_instead_of_drawing_or_uploading_one') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -121,8 +121,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('allow_to_resubmit_completed_forms') %></span> <span class="text-left"><%= t('allow_to_resubmit_completed_forms') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('allow_signers_to_resubmit_forms_after_completion_useful_when_corrections_or_multiple_submissions_are_needed') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('allow_signers_to_resubmit_forms_after_completion_useful_when_corrections_or_multiple_submissions_are_needed') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -138,8 +138,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('allow_to_decline_documents') %></span> <span class="text-left"><%= t('allow_to_decline_documents') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('allow_recipients_to_decline_signing_a_document_the_decline_reason_notification_will_be_sent_to_the_signature_requester') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('allow_recipients_to_decline_signing_a_document_the_decline_reason_notification_will_be_sent_to_the_signature_requester') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<% if !Docuseal.multitenant? || can?(:manage, :disable_decline) %> <% if !Docuseal.multitenant? || can?(:manage, :disable_decline) %>
@ -147,7 +147,7 @@
<%= f.check_box :value, class: 'toggle', checked: account_config.value != false %> <%= f.check_box :value, class: 'toggle', checked: account_config.value != false %>
</submit-form> </submit-form>
<% else %> <% else %>
<a href="<%= console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") %>" data-turbo="false" data-tip="<%= I18n.t('unlock_with_docuseal_pro') %>" data-on="change" class="flex tooltip tooltip-no-touch"> <a href="<%= console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") %>" data-turbo="false" data-tip="<%= I18n.t('unlock_with_docuseal_pro') %>" data-on="change" class="flex tooltip">
<%= f.check_box :value, class: 'toggle pointer-events-none', checked: account_config.value != false, disabled: true %> <%= f.check_box :value, class: 'toggle pointer-events-none', checked: account_config.value != false, disabled: true %>
</a> </a>
<% end %> <% end %>
@ -161,8 +161,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('allow_to_delegate_documents') %></span> <span class="text-left"><%= t('allow_to_delegate_documents') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('allow_recipients_to_delegate_documents') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('allow_recipients_to_delegate_documents') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<% if !Docuseal.multitenant? || can?(:manage, :delegate_form) %> <% if !Docuseal.multitenant? || can?(:manage, :delegate_form) %>
@ -170,7 +170,7 @@
<%= f.check_box :value, class: 'toggle', checked: account_config.value == true %> <%= f.check_box :value, class: 'toggle', checked: account_config.value == true %>
</submit-form> </submit-form>
<% else %> <% else %>
<a href="<%= console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") %>" data-turbo="false" data-tip="<%= I18n.t('unlock_with_docuseal_pro') %>" class="flex tooltip tooltip-no-touch"> <a href="<%= console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") %>" data-turbo="false" data-tip="<%= I18n.t('unlock_with_docuseal_pro') %>" class="flex tooltip">
<%= f.check_box :value, class: 'toggle pointer-events-none', checked: account_config.value == true, disabled: true %> <%= f.check_box :value, class: 'toggle pointer-events-none', checked: account_config.value == true, disabled: true %>
</a> </a>
<% end %> <% end %>
@ -184,8 +184,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('remember_and_pre_fill_signatures') %></span> <span class="text-left"><%= t('remember_and_pre_fill_signatures') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('save_a_users_signature_and_automatically_pre_fill_it_in_future_signing_sessions') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('save_a_users_signature_and_automatically_pre_fill_it_in_future_signing_sessions') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -201,8 +201,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('expirable_file_download_links') %></span> <span class="text-left"><%= t('expirable_file_download_links') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('make_document_download_links_expire_after_40_minutes_to_prevent_long_term_access_and_enhance_security') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('make_document_download_links_expire_after_40_minutes_to_prevent_long_term_access_and_enhance_security') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -218,8 +218,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('require_authentication_for_file_download_links') %></span> <span class="text-left"><%= t('require_authentication_for_file_download_links') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('require_authentication_with_user_login_or_api_key_to_access_the_document_download_links') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('require_authentication_with_user_login_or_api_key_to_access_the_document_download_links') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -235,8 +235,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('combine_completed_documents_and_audit_log') %></span> <span class="text-left"><%= t('combine_completed_documents_and_audit_log') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('combine_signed_documents_and_the_audit_log_into_a_single_pdf_file_for_easier_recordkeeping_and_compliance') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('combine_signed_documents_and_the_audit_log_into_a_single_pdf_file_for_easier_recordkeeping_and_compliance') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">
@ -253,8 +253,8 @@
<div class="flex items-center justify-between gap-4 py-2.5"> <div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('always_enforce_signing_order') %></span> <span class="text-left"><%= t('always_enforce_signing_order') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('make_the_recipients_signing_order_always_enforced_so_that_the_second_signer_can_start_signing_their_part_only_after_the_first_signer_has_completed_signing') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('make_the_recipients_signing_order_always_enforced_so_that_the_second_signer_can_start_signing_their_part_only_after_the_first_signer_has_completed_signing') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">

@ -2,7 +2,7 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow"> <div class="flex-grow">
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4"> <div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4">
<h1 class="text-3xl md:text-4xl font-bold">API</h1> <h1 class="text-4xl font-bold">API</h1>
<%= render 'shared/test_mode_toggle' %> <%= render 'shared/test_mode_toggle' %>
</div> </div>
<div class="card bg-base-200"> <div class="card bg-base-200">

@ -1,10 +1,10 @@
<form action="<%= root_path %>" method="get" id="templates_submissions_toggle" class="bg-base-200 px-1.5 rounded-xl py-1 whitespace-nowrap"> <form action="<%= root_path %>" method="get" id="templates_submissions_toggle" class="bg-base-200 px-1.5 rounded-xl py-1 whitespace-nowrap">
<toggle-cookies data-value="templates" data-key="dashboard_view" class="tooltip tooltip-top tooltip-no-touch" data-tip="<%= t('templates') %>"> <toggle-cookies data-value="templates" data-key="dashboard_view" class="sm:tooltip tooltip-top" data-tip="<%= t('templates') %>">
<button class="<%= local_assigns[:selected] == 'submissions' ? 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9' : 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>"> <button class="<%= local_assigns[:selected] == 'submissions' ? 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9' : 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>">
<%= svg_icon('layout_grid', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('layout_grid', class: 'w-6 h-6 stroke-2') %>
</button> </button>
</toggle-cookies> </toggle-cookies>
<toggle-cookies data-value="submissions" data-key="dashboard_view" class="tooltip tooltip-top tooltip-no-touch" data-tip="<%= t('submissions') %>"> <toggle-cookies data-value="submissions" data-key="dashboard_view" class="sm:tooltip tooltip-top" data-tip="<%= t('submissions') %>">
<button class="<%= local_assigns[:selected] == 'submissions' ? 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9' : 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>"> <button class="<%= local_assigns[:selected] == 'submissions' ? 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9' : 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>">
<%= svg_icon('layout_list', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('layout_list', class: 'w-6 h-6 stroke-2') %>
</button> </button>

@ -1,4 +1,4 @@
<div class="form-control"> <div class="form-control">
<%= label_tag 'user[otp_attempt]', t('two_factor_code_from_authenticator_app'), class: 'label' %> <%= label_tag 'user[otp_attempt]', t('two_factor_code_from_authenticator_app'), class: 'label' %>
<%= text_field_tag 'user[otp_attempt]', nil, autofocus: true, autocomplete: 'off', inputmode: 'numeric', placeholder: 'XXXXXX', required: true, class: 'base-input' %> <%= text_field_tag 'user[otp_attempt]', nil, autofocus: true, autocomplete: 'off', placeholder: 'XXX-XXX', required: true, class: 'base-input' %>
</div> </div>

@ -1,7 +1,7 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto min-w-0"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-4">Email SMTP</h1> <h1 class="text-4xl font-bold mb-4">Email SMTP</h1>
<% value = @encrypted_config.value || {} %> <% value = @encrypted_config.value || {} %>
<%= form_for @encrypted_config, url: settings_email_index_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for @encrypted_config, url: settings_email_index_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.fields_for :value do |ff| %> <%= f.fields_for :value do |ff| %>
@ -38,7 +38,7 @@
<% if !Docuseal.multitenant? || can?(:manage, :personalization_advanced) %> <% if !Docuseal.multitenant? || can?(:manage, :personalization_advanced) %>
<div class="form-control"> <div class="form-control">
<%= ff.label :security_label, 'SMTP Security', class: 'label' %> <%= ff.label :security_label, 'SMTP Security', class: 'label' %>
<div class="flex flex-wrap items-center gap-x-6"> <div class="flex items-center space-x-6">
<% [%w[STARTTLS none], %w[TLS tls], %w[SSL ssl], %w[Noverify noverify]].each do |(label, val)| %> <% [%w[STARTTLS none], %w[TLS tls], %w[SSL ssl], %w[Noverify noverify]].each do |(label, val)| %>
<%= ff.label :security, value: val, for: "#{val}_radio", class: 'label' do %> <%= ff.label :security, value: val, for: "#{val}_radio", class: 'label' do %>
<%= ff.radio_button :security, val, checked: (value['security'].blank? && val == 'none') || value['security'] == val, id: "#{val}_radio", class: 'base-radio mr-2' %> <%= ff.radio_button :security, val, checked: (value['security'].blank? && val == 'none') || value['security'] == val, id: "#{val}_radio", class: 'base-radio mr-2' %>

@ -1,15 +1,15 @@
<tr class="group"> <tr scope="row" class="group">
<td class="flex items-center space-x-1"> <td class="flex items-center space-x-1">
<%= svg_icon('discount_check_filled', class: 'w-6 h-6 shrink-0 text-green-500') %> <%= svg_icon('discount_check_filled', class: 'w-6 h-6 text-green-500') %>
<span class="flex items-center"> <span class="flex items-center">
<span class="whitespace-nowrap"><%= t('docuseal_trusted_signature') %></span> <%= t('docuseal_trusted_signature') %>
<div class="tooltip tooltip-top ml-1 shrink-0" data-tip="<%= t('sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature') %>"> <div class="tooltip ml-1" data-tip="<%= t('sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature') %>">
<%= svg_icon('circle_question', class: 'w-4 h-4 stroke-1 shrink-0') %> <%= svg_icon('circle_question', class: 'w-4 h-4 stroke-1') %>
</div> </div>
</span> </span>
</td> </td>
<td> <td>
<a href="<%= "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}" %>" class="btn btn-neutral btn-sm text-white whitespace-nowrap"> <a href="<%= "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}" %>" class="btn btn-neutral btn-sm text-white">
<%= t('unlock_with_docuseal_pro') %> <%= t('unlock_with_docuseal_pro') %>
</a> </a>
</td> </td>

@ -2,7 +2,7 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="md:flex-grow"> <div class="md:flex-grow">
<div class="max-w-xl"> <div class="max-w-xl">
<h1 class="text-3xl md:text-4xl font-bold mb-4"> <h1 class="text-4xl font-bold mb-4">
<%= t('pdf_signature') %> <%= t('pdf_signature') %>
</h1> </h1>
<div id="result"> <div id="result">
@ -41,7 +41,7 @@
<% end %> <% end %>
</div> </div>
<div class="flex flex-col md:flex-row gap-2 md:justify-between md:items-end mb-4 mt-8"> <div class="flex flex-col md:flex-row gap-2 md:justify-between md:items-end mb-4 mt-8">
<h2 class="text-2xl md:text-3xl font-bold"> <h2 class="text-3xl font-bold">
<%= t('signing_certificates') %> <%= t('signing_certificates') %>
</h2> </h2>
<% if can?(:create, @encrypted_config) %> <% if can?(:create, @encrypted_config) %>
@ -72,7 +72,7 @@
<tbody> <tbody>
<% @pkcs_list.each do |item| %> <% @pkcs_list.each do |item| %>
<% next if item['pkcs'].blank? %> <% next if item['pkcs'].blank? %>
<tr class="group"> <tr scope="row" class="group">
<td> <td>
<%= item['name'] %> <%= item['name'] %>
</td> </td>
@ -109,7 +109,7 @@
<% if !Docuseal.multitenant? && can?(:manage, encrypted_config) %> <% if !Docuseal.multitenant? && can?(:manage, encrypted_config) %>
<div class="flex-grow max-w-xl"> <div class="flex-grow max-w-xl">
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-2xl md:text-3xl font-bold"> <h2 class="text-3xl font-bold">
<%= t('timestamp_server') %> <%= t('timestamp_server') %>
</h2> </h2>
</div> </div>
@ -137,7 +137,7 @@
<% if can?(:manage, account_config) %> <% if can?(:manage, account_config) %>
<div class="px-1 mt-8 max-w-xl"> <div class="px-1 mt-8 max-w-xl">
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-2xl md:text-3xl font-bold"> <h2 class="text-3xl font-bold">
<%= t('preferences') %> <%= t('preferences') %>
</h2> </h2>
</div> </div>

@ -1 +1 @@
<svg class="<%= local_assigns[:class] %>" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M12 10l-6 10l-3 -5l6 -10z" /><path d="M9 15h12l-3 5h-12" /><path d="M15 15l-6 -10h6l6 10z" /></svg> <svg class="<%= local_assigns[:class] %>" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-brand-google-drive"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M12 10l-6 10l-3 -5l6 -10z" /><path d="M9 15h12l-3 5h-12" /><path d="M15 15l-6 -10h6l6 10z" /></svg>

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 458 B

@ -1,7 +1,8 @@
<svg class="<%= local_assigns[:class] %>" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg class="<%= local_assigns[:class] %>" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 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 5a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10z" /> <path d="M3 5a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10z" />
<path d="M7 20h10" /> <path d="M7 20h10" /><path d="M9 16v4" />
<path d="M15 16v4" /></svg>
<path d="M9 16v4" /> <path d="M9 16v4" />
<path d="M15 16v4" /> <path d="M15 16v4" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 497 B

@ -2,7 +2,7 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="md:flex-grow"> <div class="md:flex-grow">
<div class="flex flex-col md:flex-row md:flex-wrap gap-2 md:justify-between md:items-end mb-4 min-h-12"> <div class="flex flex-col md:flex-row md:flex-wrap gap-2 md:justify-between md:items-end mb-4 min-h-12">
<h1 class="text-3xl md:text-4xl font-bold"> <h1 class="text-4xl font-bold">
<%= t('mcp_server') %> <%= t('mcp_server') %>
</h1> </h1>
<div class="flex flex-col md:flex-row gap-y-2 gap-x-4 md:items-center"> <div class="flex flex-col md:flex-row gap-y-2 gap-x-4 md:items-center">
@ -67,7 +67,7 @@
</thead> </thead>
<tbody> <tbody>
<% @mcp_tokens.each do |mcp_token| %> <% @mcp_tokens.each do |mcp_token| %>
<tr> <tr scope="row">
<td> <td>
<%= mcp_token.name %> <%= mcp_token.name %>
</td> </td>
@ -98,8 +98,8 @@
<div class="flex items-center gap-4 py-2.5"> <div class="flex items-center gap-4 py-2.5">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="text-left"><%= t('enable_mcp_server') %></span> <span class="text-left"><%= t('enable_mcp_server') %></span>
<span class="tooltip tooltip-top hidden md:flex cursor-pointer" data-tip="<%= t('all_existing_mcp_connections_will_be_stopped_immediately_when_this_setting_is_disabled') %>"> <span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('all_existing_mcp_connections_will_be_stopped_immediately_when_this_setting_is_disabled') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span> </span>
</div> </div>
<submit-form data-on="change" class="flex"> <submit-form data-on="change" class="flex">

@ -3,10 +3,10 @@
<%= t('use_an_authenticator_mobile_app_like_google_authenticator_or_1password_to_scan_the_qr_code_below') %> <%= t('use_an_authenticator_mobile_app_like_google_authenticator_or_1password_to_scan_the_qr_code_below') %>
</p> </p>
<div> <div>
<%== RQRCode::QRCode.new(@provision_url).as_svg(viewbox: true, svg_attributes: { class: 'md:w-80 md:h-80 my-4 mx-auto' }).sub(/\A<\?xml[^>]*\?>/, '') %> <%== RQRCode::QRCode.new(@provision_url).as_svg(viewbox: true, svg_attributes: { class: 'md:w-80 md:h-80 my-4 mx-auto' }) %>
</div> </div>
<div class="form-control my-6 space-y-2"> <div class="form-control my-6 space-y-2">
<%= f.text_field :otp_attempt, required: true, inputmode: 'numeric', placeholder: 'XXXXXX', class: 'base-input text-center' %> <%= f.text_field :otp_attempt, required: true, placeholder: 'XXX-XXX', class: 'base-input text-center' %>
<span> <span>
<%= @error_message %> <%= @error_message %>
</span> </span>

@ -1,7 +1,7 @@
<%= render 'shared/turbo_modal', title: t('remove_2fa') do %> <%= render 'shared/turbo_modal', title: t('remove_2fa') do %>
<%= form_for '', url: mfa_setup_path, method: :delete, data: { turbo_frame: :_top } do |f| %> <%= form_for '', url: mfa_setup_path, method: :delete, data: { turbo_frame: :_top } do |f| %>
<div class="form-control my-6 space-y-2"> <div class="form-control my-6 space-y-2">
<%= f.text_field :otp_attempt, required: true, inputmode: 'numeric', placeholder: 'XXXXXX', class: 'base-input text-center' %> <%= f.text_field :otp_attempt, required: true, placeholder: 'XXX-XXX', class: 'base-input text-center' %>
<span> <span>
<%= @error_message %> <%= @error_message %>
</span> </span>

@ -6,7 +6,7 @@
<span> <span>
<%= t('completed_documents_notification_bcc_address') %> <%= t('completed_documents_notification_bcc_address') %>
</span> </span>
<span class="tooltip hidden md:inline-flex" data-tip="<%= t('send_email_copy_with_completed_documents_to_a_specified_bcc_address') %>"> <span class="tooltip" data-tip="<%= t('send_email_copy_with_completed_documents_to_a_specified_bcc_address') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</span> </span>

@ -1,7 +1,7 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-4"> <h1 class="text-4xl font-bold mb-4">
<%= t('email_notifications') %> <%= t('email_notifications') %>
</h1> </h1>
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
@ -22,7 +22,7 @@
</div> </div>
<%= render 'bcc_form', config: @bcc_config %> <%= render 'bcc_form', config: @bcc_config %>
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-2xl md:text-3xl font-bold"> <h2 class="text-3xl font-bold">
<%= t('sign_request_email_reminders') %> <%= t('sign_request_email_reminders') %>
</h2> </h2>
</div> </div>

@ -1,21 +1,21 @@
<div class="flex items-center px-2 py-2 border-b" style="height: 42px;"> <div class="flex items-center px-2 py-2 border-b" style="height: 42px;">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<div class="tooltip tooltip-top tooltip-no-touch before:text-xs" data-tip="<%= t('bold') %> (Ctrl+B)"> <div class="tooltip tooltip-top before:text-xs" data-tip="<%= t('bold') %> (Ctrl+B)">
<button type="button" data-action="click:<%= editor_tag %>#bold" data-target="<%= editor_tag %>.boldButton" aria-label="<%= t('bold') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300"> <button type="button" data-action="click:<%= editor_tag %>#bold" data-target="<%= editor_tag %>.boldButton" aria-label="<%= t('bold') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300">
<%= svg_icon('bold', class: 'w-4 h-4') %> <%= svg_icon('bold', class: 'w-4 h-4') %>
</button> </button>
</div> </div>
<div class="tooltip tooltip-top tooltip-no-touch before:text-xs" data-tip="<%= t('italic') %> (Ctrl+I)"> <div class="tooltip tooltip-top before:text-xs" data-tip="<%= t('italic') %> (Ctrl+I)">
<button type="button" data-action="click:<%= editor_tag %>#italic" data-target="<%= editor_tag %>.italicButton" aria-label="<%= t('italic') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300"> <button type="button" data-action="click:<%= editor_tag %>#italic" data-target="<%= editor_tag %>.italicButton" aria-label="<%= t('italic') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300">
<%= svg_icon('italic', class: 'w-4 h-4') %> <%= svg_icon('italic', class: 'w-4 h-4') %>
</button> </button>
</div> </div>
<div class="tooltip tooltip-top tooltip-no-touch before:text-xs" data-tip="<%= t('underline') %> (Ctrl+U)"> <div class="tooltip tooltip-top before:text-xs" data-tip="<%= t('underline') %> (Ctrl+U)">
<button type="button" data-action="click:<%= editor_tag %>#underline" data-target="<%= editor_tag %>.underlineButton" aria-label="<%= t('underline') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300"> <button type="button" data-action="click:<%= editor_tag %>#underline" data-target="<%= editor_tag %>.underlineButton" aria-label="<%= t('underline') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300">
<%= svg_icon('underline', class: 'w-4 h-4') %> <%= svg_icon('underline', class: 'w-4 h-4') %>
</button> </button>
</div> </div>
<div class="tooltip tooltip-top tooltip-no-touch before:text-xs" data-tip="<%= t('link') %> (Ctrl+K)"> <div class="tooltip tooltip-top before:text-xs" data-tip="<%= t('link') %> (Ctrl+K)">
<button type="button" data-action="click:<%= editor_tag %>#linkSelection" data-target="<%= editor_tag %>.linkButton" aria-label="<%= t('link') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300"> <button type="button" data-action="click:<%= editor_tag %>#linkSelection" data-target="<%= editor_tag %>.linkButton" aria-label="<%= t('link') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300">
<%= svg_icon('link', class: 'w-4 h-4') %> <%= svg_icon('link', class: 'w-4 h-4') %>
</button> </button>
@ -23,12 +23,12 @@
</div> </div>
<div class="mx-2 h-5 border-l border-base-content/20"></div> <div class="mx-2 h-5 border-l border-base-content/20"></div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<div class="tooltip tooltip-top tooltip-no-touch before:text-xs" data-tip="<%= t('undo') %> (Ctrl+Z)"> <div class="tooltip tooltip-top before:text-xs" data-tip="<%= t('undo') %> (Ctrl+Z)">
<button type="button" data-action="click:<%= editor_tag %>#undo" data-target="<%= editor_tag %>.undoButton" aria-label="<%= t('undo') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300"> <button type="button" data-action="click:<%= editor_tag %>#undo" data-target="<%= editor_tag %>.undoButton" aria-label="<%= t('undo') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300">
<%= svg_icon('arrow_back_up', class: 'w-4 h-4') %> <%= svg_icon('arrow_back_up', class: 'w-4 h-4') %>
</button> </button>
</div> </div>
<div class="tooltip tooltip-top tooltip-no-touch before:text-xs" data-tip="<%= t('redo') %> (Ctrl+Shift+Z)"> <div class="tooltip tooltip-top before:text-xs" data-tip="<%= t('redo') %> (Ctrl+Shift+Z)">
<button type="button" data-action="click:<%= editor_tag %>#redo" data-target="<%= editor_tag %>.redoButton" aria-label="<%= t('redo') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300"> <button type="button" data-action="click:<%= editor_tag %>#redo" data-target="<%= editor_tag %>.redoButton" aria-label="<%= t('redo') %>" class="flex items-center px-1 w-6 h-6 rounded hover:bg-base-300">
<%= svg_icon('arrow_forward_up', class: 'w-4 h-4') %> <%= svg_icon('arrow_forward_up', class: 'w-4 h-4') %>
</button> </button>
@ -37,9 +37,8 @@
<% if local_assigns[:variables]&.any? %> <% if local_assigns[:variables]&.any? %>
<% variable_labels = { 'account.name' => t('variables.account_name'), 'submitter.link' => t('variables.submitter_link'), 'template.name' => t('variables.template_name'), 'submission.submitters' => t('variables.submission_submitters'), 'submission.link' => t('variables.submission_link'), 'documents.link' => t('variables.documents_link') } %> <% variable_labels = { 'account.name' => t('variables.account_name'), 'submitter.link' => t('variables.submitter_link'), 'template.name' => t('variables.template_name'), 'submission.submitters' => t('variables.submission_submitters'), 'submission.link' => t('variables.submission_link'), 'documents.link' => t('variables.documents_link') } %>
<div class="dropdown dropdown-end ml-auto"> <div class="dropdown dropdown-end ml-auto">
<label tabindex="0" class="flex items-center gap-1 text-sm px-2 py-1 rounded hover:bg-base-200 cursor-pointer whitespace-nowrap"> <label tabindex="0" class="flex items-center gap-1 text-sm px-2 py-1 rounded hover:bg-base-200 cursor-pointer">
<span class="hidden sm:inline"><%= t('add_variable') %></span> <%= t('add_variable') %>
<span class="sm:hidden"><%= t('variable') %></span>
<%= svg_icon('chevron_down', class: 'w-3.5 h-3.5') %> <%= svg_icon('chevron_down', class: 'w-3.5 h-3.5') %>
</label> </label>
<div tabindex="0" class="dropdown-content right-0 top-full mt-1 p-1 bg-white border border-neutral-200 rounded-lg shadow-lg z-50"> <div tabindex="0" class="dropdown-content right-0 top-full mt-1 p-1 bg-white border border-neutral-200 rounded-lg shadow-lg z-50">

@ -1,7 +1,7 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<p class="text-3xl md:text-4xl font-bold mb-4"> <p class="text-4xl font-bold mb-4">
<%= t('email_templates') %> <%= t('email_templates') %>
</p> </p>
<div class="space-y-4"> <div class="space-y-4">
@ -9,11 +9,11 @@
<%= render 'documents_copy_email_form' %> <%= render 'documents_copy_email_form' %>
<%= render 'submitter_completed_email_form' %> <%= render 'submitter_completed_email_form' %>
</div> </div>
<p class="text-3xl md:text-4xl font-bold mb-4 mt-8"> <p class="text-4xl font-bold mb-4 mt-8">
<%= t('company_logo') %> <%= t('company_logo') %>
</p> </p>
<%= render 'logo_form' %> <%= render 'logo_form' %>
<p class="text-3xl md:text-4xl font-bold mb-4 mt-8"> <p class="text-4xl font-bold mb-4 mt-8">
<%= t('submission_form') %> <%= t('submission_form') %>
</p> </p>
<div class="space-y-4"> <div class="space-y-4">

@ -1,7 +1,7 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-4"> <h1 class="text-4xl font-bold mb-4">
<%= t('profile') %> <%= t('profile') %>
</h1> </h1>
<%= form_for current_user, url: update_contact_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for current_user, url: update_contact_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>

@ -1,7 +0,0 @@
<div class="mb-2 w-full">
<a href="<%= root_path %>" class="flex items-center space-x-0.5 font-medium">
<%= svg_icon('arrow_left', class: 'w-4 h-4 stroke-2') %>
<span><%= t('back') %></span>
</a>
</div>
<%= render 'shared/settings_nav', expanded: true %>

@ -1 +1 @@
<% 'stats stat stat-figure stat-title stat-value text-accent w-fit hover:bg-white dropdown-open border-base-content/30 before:border-base-content/30 order-1 order-2 order-3 order-4 order-5 lg:inline lg:hidden' %> <% 'stats stat stat-figure stat-title stat-value text-accent w-fit hover:bg-white dropdown-open border-base-content/30 before:border-base-content/30' %>

@ -1,4 +1,4 @@
<a target="_blank" href="<%= Docuseal::GITHUB_URL %>" rel="noopener noreferrer nofollow" class="relative flex items-center rounded-full px-2 py-0.5 text-xs leading-4 mt-1 text-base-content border border-base-300 tooltip tooltip-bottom tooltip-no-touch" data-tip="Give a star on GitHub"> <a target="_blank" href="<%= Docuseal::GITHUB_URL %>" rel="noopener noreferrer nofollow" class="relative flex items-center rounded-full px-2 py-0.5 text-xs leading-4 mt-1 text-base-content border border-base-300 tooltip tooltip-bottom" data-tip="Give a star on GitHub">
<span class="flex items-center justify-between space-x-0.5 font-medium"> <span class="flex items-center justify-between space-x-0.5 font-medium">
<%= svg_icon('start', class: 'h-3 w-3') %> <%= svg_icon('start', class: 'h-3 w-3') %>
<span>18k</span> <span>18k</span>

@ -28,13 +28,7 @@
<span class="text-sm align-text-top"><%= current_user.initials %></span> <span class="text-sm align-text-top"><%= current_user.initials %></span>
</label> </label>
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box min-w-[160px] text-right"> <ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box min-w-[160px] text-right">
<li class="md:hidden"> <li>
<%= link_to settings_path, class: 'flex items-center' do %>
<%= svg_icon('adjustments', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
<span class="mr-1"><%= t('settings') %></span>
<% end %>
</li>
<li class="hidden md:list-item">
<%= link_to settings_profile_index_path, class: 'flex items-center' do %> <%= link_to settings_profile_index_path, class: 'flex items-center' do %>
<%= svg_icon('adjustments', class: 'w-5 h-5 flex-shrink-0 stroke-2') %> <%= svg_icon('adjustments', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
<span class="mr-1"><%= t('profile') %></span> <span class="mr-1"><%= t('profile') %></span>

@ -1,17 +1,14 @@
<% if pagy.pages > 1 %> <% if pagy.pages > 1 %>
<% left_additional_html = local_assigns[:left_additional_html] %> <div class="flex my-6 justify-center md:justify-between">
<div class="flex flex-col md:flex-row md:items-center <%= left_additional_html.present? ? 'mt-3' : 'mt-6' %> mb-6 md:my-6 justify-center md:justify-between"> <div class="hidden md:block text-sm">
<div class="text-sm <%= 'mb-2 md:mb-0' if left_additional_html.present? %>">
<span class="hidden md:inline">
<% if pagy.count.nil? %> <% if pagy.count.nil? %>
<%= t("pagination.#{local_assigns.fetch(:items_name, 'items')}.range_without_total", from: local_assigns.fetch(:from, pagy.from), to: local_assigns.fetch(:to, pagy.to)) %> <%= t("pagination.#{local_assigns.fetch(:items_name, 'items')}.range_without_total", from: local_assigns.fetch(:from, pagy.from), to: local_assigns.fetch(:to, pagy.to)) %>
<% else %> <% else %>
<%= t("pagination.#{local_assigns.fetch(:items_name, 'items')}.range_with_total", from: local_assigns.fetch(:from, pagy.from), to: local_assigns.fetch(:to, pagy.to), count: local_assigns.fetch(:count, pagy.count)) %> <%= t("pagination.#{local_assigns.fetch(:items_name, 'items')}.range_with_total", from: local_assigns.fetch(:from, pagy.from), to: local_assigns.fetch(:to, pagy.to), count: local_assigns.fetch(:count, pagy.count)) %>
<% end %> <% end %>
</span> <%= local_assigns[:left_additional_html] %>
<%= left_additional_html %>
</div> </div>
<div class="flex items-center space-x-1.5 justify-center md:justify-end"> <div class="flex items-center space-x-1.5">
<%= local_assigns[:right_additional_html] %> <%= local_assigns[:right_additional_html] %>
<div class="join"> <div class="join">
<% if pagy.previous %> <% if pagy.previous %>

@ -15,7 +15,7 @@
</div> </div>
<% end %> <% end %>
<search-input class="group flex items-center max-md:focus-within:flex-1 <%= 'max-md:flex-1' if params[:q].present? %>"> <search-input class="group flex items-center max-md:focus-within:flex-1 <%= 'max-md:flex-1' if params[:q].present? %>">
<input id="search" name="q" value="<%= params[:q] %>" enterkeyhint="search" class="input text-lg pr-10 -mr-12 w-0 md:w-60 max-md:group-focus-within:w-full <%= 'pl-8 input-outlined w-full' if params[:q].present? %>" placeholder="<%= local_assigns[:placeholder] %>"> <input id="search" name="q" value="<%= params[:q] %>" enterkeyhint="search" class="input text-lg pr-10 -mr-12 w-0 md:w-60 max-md:group-focus-within:w-full <%= 'pl-8 input-outlined max-md:w-full' if params[:q].present? %>" placeholder="<%= local_assigns[:placeholder] %>">
<button type="submit" title="<%= t('search') %>" class="btn btn-ghost btn-circle"> <button type="submit" title="<%= t('search') %>" class="btn btn-ghost btn-circle">
<span class="enabled"> <span class="enabled">
<%= svg_icon('search', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('search', class: 'w-6 h-6 stroke-2') %>

@ -1,24 +1,7 @@
<% expanded = local_assigns[:expanded] %> <div class="block w-full md:w-52 flex-none">
<div class="<%= expanded ? 'block -mx-3 md:mx-0' : 'w-full md:w-52 flex-none' %>">
<% unless expanded %>
<div class="md:hidden w-full">
<a href="<%= settings_path %>" class="flex items-center space-x-0.5 font-medium">
<%= svg_icon('arrow_left', class: 'w-4 h-4 stroke-2') %>
<span><%= t('settings') %></span>
</a>
</div>
<% end %>
<div class="<%= 'hidden md:block' unless expanded %>">
<menu-active> <menu-active>
<ul id="account_settings_menu" class="menu px-0"> <ul id="account_settings_menu" class="menu px-0">
<% unless expanded %> <li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium flex items-center space-x-0.5"><%= svg_icon('arrow_left', class: 'w-4 h-4 stroke-2') %><span><%= t('back') %></span></a></li>
<li class="menu-title py-0 !bg-transparent mb-3 -mt-5">
<a href="<%= root_path %>" class="!bg-transparent !text-neutral font-medium flex items-center space-x-0.5">
<%= svg_icon('arrow_left', class: 'w-4 h-4 stroke-2') %>
<span><%= t('back') %></span>
</a>
</li>
<% end %>
<li class="menu-title py-0 !bg-transparent"> <li class="menu-title py-0 !bg-transparent">
<span class="!bg-transparent"><%= t('settings') %></span> <span class="!bg-transparent"><%= t('settings') %></span>
</li> </li>
@ -131,30 +114,30 @@
</ul> </ul>
</menu-active> </menu-active>
<% if Docuseal.multitenant? || cannot?(:manage, :tenants) %> <% if Docuseal.multitenant? || cannot?(:manage, :tenants) %>
<div id="support_channels" class="mx-4 border-t border-base-300 <%= 'hidden md:block' unless expanded %>"> <div id="support_channels" class="mx-4 border-t border-base-300 hidden md:block">
<div class="text-sm mt-3"> <div class="text-sm mt-3">
<%= t('need_help_ask_a_question_') %> <%= t('need_help_ask_a_question_') %>
</div> </div>
<div class="flex mt-3 space-x-3"> <div class="flex mt-3 space-x-3">
<div class="tooltip tooltip-no-touch" data-tip="GitHub"> <div class="tooltip" data-tip="GitHub">
<a href="<%= Docuseal::GITHUB_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md"> <a href="<%= Docuseal::GITHUB_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md">
<%= svg_icon('brand_github', class: 'w-8 h-8') %> <%= svg_icon('brand_github', class: 'w-8 h-8') %>
</a> </a>
</div> </div>
<div class="tooltip tooltip-no-touch" data-tip="<%= t('discord_community') %>"> <div class="tooltip" data-tip="<%= t('discord_community') %>">
<a href="<%= Docuseal::DISCORD_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md"> <a href="<%= Docuseal::DISCORD_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md">
<%= svg_icon('brand_discord', class: 'w-8 h-8') %> <%= svg_icon('brand_discord', class: 'w-8 h-8') %>
</a> </a>
</div> </div>
<%= capture do %> <%= capture do %>
<div class="tooltip tooltip-no-touch" data-tip="<%= t('ai_assistant') %>"> <div class="tooltip" data-tip="<%= t('ai_assistant') %>">
<a href="<%= Docuseal::CHATGPT_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md"> <a href="<%= Docuseal::CHATGPT_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md">
<%= svg_icon('message', class: 'w-8 h-8') %> <%= svg_icon('message', class: 'w-8 h-8') %>
</a> </a>
</div> </div>
<% end %> <% end %>
</div> </div>
<a href="mailto:<%= Docuseal::SUPPORT_EMAIL %>" target="_blank" class="w-full block mt-4 underline"> <a href="mailto:<%= Docuseal::SUPPORT_EMAIL %>" target="_blank" class="w-full block mt-4 underline text-center">
<%= Docuseal::SUPPORT_EMAIL %> <%= Docuseal::SUPPORT_EMAIL %>
</a> </a>
<% if Docuseal.version.present? && !Docuseal.multitenant? && can?(:manage, EncryptedConfig) %> <% if Docuseal.version.present? && !Docuseal.multitenant? && can?(:manage, EncryptedConfig) %>
@ -165,4 +148,3 @@
</div> </div>
<% end %> <% end %>
</div> </div>
</div>

@ -1,8 +1,7 @@
<% offset = local_assigns[:title] ? '45px' : '0px' %>
<turbo-frame id="drawer"> <turbo-frame id="drawer">
<turbo-modal class="modal modal-open items-start !animate-none justify-end overflow-y-auto !justify-normal md:!justify-end" data-close-after-submit="<%= local_assigns.key?(:close_after_submit) ? local_assigns[:close_after_submit] : true %>"> <turbo-modal class="modal modal-open items-start !animate-none justify-end overflow-y-auto !justify-normal md:!justify-end" data-close-after-submit="<%= local_assigns.key?(:close_after_submit) ? local_assigns[:close_after_submit] : true %>">
<div class="absolute top-0 bottom-0 right-0 left-0" data-action="click:turbo-modal#close"></div> <div class="absolute top-0 bottom-0 right-0 left-0" data-action="click:turbo-modal#close"></div>
<div class="bg-base-100 min-h-screen max-h-screen relative w-full min-w-0"> <div class="bg-base-100 min-h-screen max-h-screen relative w-full relative">
<% if local_assigns[:title] %> <% if local_assigns[:title] %>
<div class="flex justify-between bg-base-100 py-2 px-4 items-center border-b pb-2 font-medium"> <div class="flex justify-between bg-base-100 py-2 px-4 items-center border-b pb-2 font-medium">
<span> <span>
@ -15,7 +14,7 @@
&times; &times;
</span> </span>
<% end %> <% end %>
<div class="md:w-[620px] overflow-y-auto" style="max-height: calc(100vh - <%= offset %>); max-height: calc(100dvh - <%= offset %>)"> <div class="w-screen md:w-[620px] overflow-y-auto" style="max-height: calc(100vh - <%= local_assigns[:title] ? '45px' : '0px' %>)">
<%= yield %> <%= yield %>
</div> </div>
</div> </div>

@ -1,4 +1,3 @@
<% if turbo_frame_request? %>
<turbo-frame id="modal"> <turbo-frame id="modal">
<turbo-modal class="modal modal-open items-start !animate-none overflow-y-auto !justify-normal md:!justify-center" data-close-after-submit="<%= local_assigns.key?(:close_after_submit) ? local_assigns[:close_after_submit] : true %>"> <turbo-modal class="modal modal-open items-start !animate-none overflow-y-auto !justify-normal md:!justify-center" data-close-after-submit="<%= local_assigns.key?(:close_after_submit) ? local_assigns[:close_after_submit] : true %>">
<div class="absolute top-0 bottom-0 right-0 left-0" data-action="click:turbo-modal#close"></div> <div class="absolute top-0 bottom-0 right-0 left-0" data-action="click:turbo-modal#close"></div>
@ -11,24 +10,9 @@
<a href="#" class="text-xl" data-action="click:turbo-modal#close">&times;</a> <a href="#" class="text-xl" data-action="click:turbo-modal#close">&times;</a>
</div> </div>
<% end %> <% end %>
<div class="w-full md:w-[590px] overflow-y-auto" style="max-height: calc(100vh - 14px - 45px); max-height: calc(100dvh - 14px - 45px)"> <div class="w-full md:w-[590px] overflow-y-auto" style="max-height: calc(100vh - 14px - 45px)">
<%= yield %> <%= yield %>
</div> </div>
</div> </div>
</turbo-modal> </turbo-modal>
</turbo-frame> </turbo-frame>
<% else %>
<% if local_assigns[:title] %>
<div class="sticky top-0 z-10 flex justify-between bg-base-100 py-2 px-5 items-center border-b pb-2 font-medium">
<span>
<%= local_assigns[:title] %>
</span>
<history-back>
<a href="#" class="text-xl block py-2 px-5 -my-2 -mr-5">&times;</a>
</history-back>
</div>
<% end %>
<div class="w-full">
<%= yield %>
</div>
<% end %>

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

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

@ -29,7 +29,7 @@
<%= f.hidden_field 'submitter[name]', value: params[:name] || @submitter&.name %> <%= f.hidden_field 'submitter[name]', value: params[:name] || @submitter&.name %>
<%= f.hidden_field 'submitter[email]', value: params[:email] || @submitter&.email %> <%= f.hidden_field 'submitter[email]', value: params[:email] || @submitter&.email %>
<%= f.hidden_field 'submitter[phone]', value: params[:phone] || @submitter&.phone %> <%= f.hidden_field 'submitter[phone]', value: params[:phone] || @submitter&.phone %>
<%= f.text_field :one_time_code, required: true, inputmode: 'numeric', class: 'base-input text-center', placeholder: 'XXXXXX' %> <%= f.text_field :one_time_code, required: true, class: 'base-input text-center', placeholder: 'XXX-XXX' %>
<div class="flex justify-between items-center mt-1"> <div class="flex justify-between items-center mt-1">
<span> <span>
<% if flash[:alert] %> <% if flash[:alert] %>

@ -1,7 +1,7 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-4"> <h1 class="text-4xl font-bold mb-4">
<%= t('storage') %> <%= t('storage') %>
</h1> </h1>
<% value = @encrypted_config.value || { 'service' => 'disk' } %> <% value = @encrypted_config.value || { 'service' => 'disk' } %>

@ -45,8 +45,8 @@
<% default_subject = template&.preferences&.dig('request_email_subject').presence || config.value['subject'] %> <% default_subject = template&.preferences&.dig('request_email_subject').presence || config.value['subject'] %>
<% default_body = template&.preferences&.dig('request_email_body').presence || config_body %> <% default_body = template&.preferences&.dig('request_email_body').presence || config_body %>
<% is_edit_viewer = local_assigns[:submitter] && local_assigns[:viewer_submitter_uuids].include?(local_assigns[:submitter].uuid) %> <% is_edit_viewer = local_assigns[:submitter] && local_assigns[:viewer_submitter_uuids].include?(local_assigns[:submitter].uuid) %>
<div id="<%= message_field_id %>" class="relative flex flex-col rounded-2xl md:bg-base-300/40 hidden"> <div id="<%= message_field_id %>" class="card card-compact bg-base-300/40 hidden">
<div class="flex flex-auto flex-col gap-2 md:text-sm md:p-4"> <div class="card-body">
<%= tag.input id: toggle_uuid = SecureRandom.uuid, value: '1', name: 'request_email_per_submitter', class: 'peer', type: 'checkbox', hidden: true, checked: local_assigns[:message_per_submitter] != false && template_submitters.size < 11 && template&.preferences&.dig('submitters').to_a.size > 1 %> <%= tag.input id: toggle_uuid = SecureRandom.uuid, value: '1', name: 'request_email_per_submitter', class: 'peer', type: 'checkbox', hidden: true, checked: local_assigns[:message_per_submitter] != false && template_submitters.size < 11 && template&.preferences&.dig('submitters').to_a.size > 1 %>
<% if local_assigns[:viewer_submitter_uuids].present? && local_assigns[:submitter].blank? %> <% if local_assigns[:viewer_submitter_uuids].present? && local_assigns[:submitter].blank? %>
<% (template_submitters.pluck('uuid') - local_assigns[:viewer_submitter_uuids].to_a).each do |signer_uuid| %> <% (template_submitters.pluck('uuid') - local_assigns[:viewer_submitter_uuids].to_a).each do |signer_uuid| %>

@ -1,5 +1,5 @@
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
<div class="tooltip tooltip-no-touch w-full" data-tip="<%= t('unlock_with_docuseal_pro') %>"> <div class="tooltip w-full" data-tip="<%= t('unlock_with_docuseal_pro') %>">
<%= link_to submitter.sent_at? ? t('re_send_sms') : t('send_sms'), Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}", class: 'btn btn-sm btn-primary text-gray-400 w-full' %> <%= link_to submitter.sent_at? ? t('re_send_sms') : t('send_sms'), Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}", class: 'btn btn-sm btn-primary text-gray-400 w-full' %>
</div> </div>
</div> </div>

@ -25,12 +25,10 @@
<span class="hidden md:inline"><%= t('audit_log') %></span> <span class="hidden md:inline"><%= t('audit_log') %></span>
</a> </a>
<% elsif signed_in? %> <% elsif signed_in? %>
<open-modal-mobile class="flex">
<%= link_to submission_events_path(@submission), class: 'white-button', data: { turbo_frame: :modal } do %> <%= link_to submission_events_path(@submission), class: 'white-button', data: { turbo_frame: :modal } do %>
<%= svg_icon('logs', class: 'w-6 h-6') %> <%= svg_icon('logs', class: 'w-6 h-6') %>
<span class="hidden md:block"><%= t('event_log') %></span> <span class="hidden md:block"><%= t('event_log') %></span>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
<% if signed_in? && !@submission.completed_at? && can?(:manage, :resend_all) && @submission.submitters.to_a.size > 3 && !@submission.archived_at? && !@submission.template&.archived_at? && !@submission.expired? && can?(:update, @submission) %> <% if signed_in? && !@submission.completed_at? && can?(:manage, :resend_all) && @submission.submitters.to_a.size > 3 && !@submission.archived_at? && !@submission.template&.archived_at? && !@submission.expired? && can?(:update, @submission) %>
<% pending_submitters_count = @submission.submitters.to_a.count { |s| !s.completed_at? && s.email.present? && !s.declined_at? } %> <% pending_submitters_count = @submission.submitters.to_a.count { |s| !s.completed_at? && s.email.present? && !s.declined_at? } %>
@ -182,12 +180,10 @@
</span> </span>
</div> </div>
<% if signed_in? && can?(:update, @submission) && submitter && !submitter.completed_at? && !@submission.completed_at? && !submitter.declined_at? && !@submission.archived_at? && !@submission.template&.archived_at? && !@submission.expired? && !submitter.start_form_submission_events.any? %> <% if signed_in? && can?(:update, @submission) && submitter && !submitter.completed_at? && !@submission.completed_at? && !submitter.declined_at? && !@submission.archived_at? && !@submission.template&.archived_at? && !@submission.expired? && !submitter.start_form_submission_events.any? %>
<span class="tooltip tooltip-left tooltip-no-touch" data-tip="<%= t('edit') %>"> <span class="tooltip tooltip-left" data-tip="<%= t('edit') %>">
<open-modal-mobile>
<%= link_to edit_submitter_path(submitter), class: 'shrink-0 inline md:hidden md:group-hover:inline', data: { turbo_frame: 'modal' } do %> <%= link_to edit_submitter_path(submitter), class: 'shrink-0 inline md:hidden md:group-hover:inline', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('pencil', class: 'w-5 h-5') %> <%= svg_icon('pencil', class: 'w-5 h-5') %>
<% end %> <% end %>
</open-modal-mobile>
</span> </span>
<% end %> <% end %>
</div> </div>

@ -16,8 +16,8 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="flex items-center md:items-end gap-2"> <div class="flex flex-col items-end md:flex-row gap-2">
<%= render 'submissions_filters/applied_filters', filter_params:, with_status: true %> <%= render 'submissions_filters/applied_filters', filter_params: %>
<%= render 'submissions_filters/filter_button', filter_params: %> <%= render 'submissions_filters/filter_button', filter_params: %>
</div> </div>
</div> </div>

@ -18,12 +18,10 @@
<span class="hidden sm:block"> <span class="hidden sm:block">
<%= render 'templates/upload_button' %> <%= render 'templates/upload_button' %>
</span> </span>
<open-modal-mobile data-disabled="<%= !Docuseal.multitenant? %>" class="flex">
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %> <%= 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') %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:block"><%= t('create') %></span> <span class="hidden md:block"><%= t('create') %></span>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
</div> </div>
</div> </div>
@ -36,7 +34,7 @@
<% end %> <% end %>
<% if is_show_tabs %> <% if is_show_tabs %>
<div class="flex items-center flex-col md:flex-row md:flex-wrap gap-2 mb-4"> <div class="flex items-center flex-col md:flex-row md:flex-wrap gap-2 mb-4">
<div class="hidden md:flex md:items-end gap-2 md:w-fit"> <div class="flex items-center md:items-end flex-col md:flex-row gap-2 w-full md:w-fit">
<a href="<%= url_for(params: request.query_parameters.except('status')) %>" class="<%= params[:status].blank? ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600"> <a href="<%= url_for(params: request.query_parameters.except('status')) %>" class="<%= params[:status].blank? ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<%= svg_icon('list', class: 'w-5 h-5') %> <%= svg_icon('list', class: 'w-5 h-5') %>
@ -56,8 +54,8 @@
</div> </div>
</a> </a>
</div> </div>
<div class="flex items-center md:items-end 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:, with_status: true %> <%= 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: %> <%= render 'submissions_filters/actions', filter_params: %>
</div> </div>

@ -1,17 +1,8 @@
<% query_params = params.permit(:q).merge(filter_params) %> <% query_params = params.permit(:q).merge(filter_params) %>
<% filter_names = %w[status folder author completed_at created_at] %> <% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit', 'sent' => 'send', 'opened' => 'mail_opened' }[params[:status]] %>
<% ordered_filters = request.query_parameters.keys.filter_map { |key| filter_names.find { |name| key.start_with?(name) } }.uniq %> <div class="flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-36 border-neutral-700">
<% chip_order = ordered_filters.reverse.each_with_index.to_h { |name, index| [name, index + 1] } %>
<% chip_order.default = ordered_filters.size + 1 %>
<% status_icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit', 'sent' => 'send', 'opened' => 'mail_opened' }[params[:status]] %>
<% status_icons = { 'all' => 'list', 'pending' => 'clock', 'completed' => 'circle_check' } %>
<% current_status = status_icons.key?(params[:status].to_s) ? params[:status].to_s : 'all' %>
<% with_status_button = local_assigns[:with_status] && status_icon.blank? %>
<% chips_html = capture do %>
<% if status_icon %>
<div class="order-none flex h-10 px-2 py-1 text-base md:text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl shrink-0 max-w-[60vw] md:w-36 border-neutral-700">
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> <%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %>
<%= svg_icon(status_icon, class: 'w-5 h-5 shrink-0') %> <%= svg_icon(icon, class: 'w-5 h-5 shrink-0') %>
<span class="font-normal truncate"><%= t(params[:status]) %></span> <span class="font-normal truncate"><%= t(params[:status]) %></span>
<% end %> <% end %>
<%= link_to url_for(params: request.query_parameters.except('status')), class: 'rounded-lg ml-1 shrink-0 hover:bg-base-content hover:text-white' do %> <%= link_to url_for(params: request.query_parameters.except('status')), class: 'rounded-lg ml-1 shrink-0 hover:bg-base-content hover:text-white' do %>
@ -20,7 +11,7 @@
</div> </div>
<% end %> <% end %>
<% if params[:folder].present? %> <% if params[:folder].present? %>
<div class="order-<%= chip_order['folder'] %> md:order-none md:tooltip tooltip-no-touch tooltip-bottom flex md:flex h-10 px-2 py-1 text-base md:text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl shrink-0 max-w-[60vw] md:w-36 border-neutral-700" data-tip="<%= t('folder') %>"> <div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-36 border-neutral-700" data-tip="<%= t('folder') %>">
<%= link_to submissions_filter_path('folder', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> <%= link_to submissions_filter_path('folder', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %>
<%= svg_icon('folder', class: 'w-5 h-5 shrink-0') %> <%= svg_icon('folder', class: 'w-5 h-5 shrink-0') %>
<span class="font-normal truncate"><%= params[:folder] %></span> <span class="font-normal truncate"><%= params[:folder] %></span>
@ -31,7 +22,7 @@
</div> </div>
<% end %> <% end %>
<% if params[:author].present? %> <% if params[:author].present? %>
<div class="order-<%= chip_order['author'] %> md:order-none md:tooltip tooltip-no-touch tooltip-bottom flex md:flex h-10 px-2 py-1 text-base md:text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl shrink-0 max-w-[60vw] md:w-36 border-neutral-700" data-tip="<%= t('author') %>"> <div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-36 border-neutral-700" data-tip="<%= t('author') %>">
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> <%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %>
<%= svg_icon('user', class: 'w-5 h-5 shrink-0') %> <%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
<span class="font-normal truncate"><%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %></span> <span class="font-normal truncate"><%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %></span>
@ -42,14 +33,15 @@
</div> </div>
<% end %> <% end %>
<% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %> <% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %>
<div class="order-<%= chip_order['completed_at'] %> md:order-none md:tooltip tooltip-no-touch tooltip-bottom flex md:flex h-10 px-2 py-1 text-base md:text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl shrink-0 max-w-[60vw] md:w-36 border-neutral-700" data-tip="<%= t('completed_at') %>"> <div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-36 border-neutral-700" data-tip="<%= t('completed_at') %>">
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> <%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %>
<%= svg_icon('calendar_check', class: 'w-5 h-5 shrink-0') %> <%= svg_icon('calendar_check', class: 'w-5 h-5 shrink-0') %>
<span class="flex flex-col font-normal text-left md:text-center text-xs"> <span class="flex flex-row md:flex-col font-normal text-left md:text-center md:text-xs">
<% if query_params[:completed_at_from] == query_params[:completed_at_to] %> <% if query_params[:completed_at_from] == query_params[:completed_at_to] %>
<span><%= l(Date.parse(query_params[:completed_at_from]), locale: current_account.locale) %></span> <span><%= l(Date.parse(query_params[:completed_at_from]), locale: current_account.locale) %></span>
<% else %> <% else %>
<span><%= query_params[:completed_at_from].present? ? l(Date.parse(query_params[:completed_at_from]), locale: current_account.locale) : '∞' %></span> <span><%= query_params[:completed_at_from].present? ? l(Date.parse(query_params[:completed_at_from]), locale: current_account.locale) : '∞' %></span>
<span class="px-1 md:px-0 md:hidden">-</span>
<span><%= query_params[:completed_at_to].present? ? l(Date.parse(query_params[:completed_at_to]), locale: current_account.locale) : t('today') %></span> <span><%= query_params[:completed_at_to].present? ? l(Date.parse(query_params[:completed_at_to]), locale: current_account.locale) : t('today') %></span>
<% end %> <% end %>
</span> </span>
@ -60,14 +52,15 @@
</div> </div>
<% end %> <% end %>
<% if query_params[:created_at_from].present? || query_params[:created_at_to].present? %> <% if query_params[:created_at_from].present? || query_params[:created_at_to].present? %>
<div class="order-<%= chip_order['created_at'] %> md:order-none md:tooltip tooltip-no-touch tooltip-bottom flex md:flex h-10 px-2 py-1 text-base md:text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl shrink-0 max-w-[60vw] md:w-36 border-neutral-700" data-tip="<%= t('created_at') %>"> <div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-36 border-neutral-700" data-tip="<%= t('created_at') %>">
<%= link_to submissions_filter_path('created_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> <%= link_to submissions_filter_path('created_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %>
<%= svg_icon('calendar', class: 'w-5 h-5 shrink-0') %> <%= svg_icon('calendar', class: 'w-5 h-5 shrink-0') %>
<span class="flex flex-col font-normal text-left md:text-center text-xs"> <span class="flex flex-row md:flex-col font-normal text-left md:text-center md:text-xs">
<% if query_params[:created_at_from] == query_params[:created_at_to] %> <% if query_params[:created_at_from] == query_params[:created_at_to] %>
<span><%= l(Date.parse(query_params[:created_at_from]), locale: current_account.locale) %></span> <span><%= l(Date.parse(query_params[:created_at_from]), locale: current_account.locale) %></span>
<% else %> <% else %>
<span><%= query_params[:created_at_from].present? ? l(Date.parse(query_params[:created_at_from]), locale: current_account.locale) : '∞' %></span> <span><%= query_params[:created_at_from].present? ? l(Date.parse(query_params[:created_at_from]), locale: current_account.locale) : '∞' %></span>
<span class="px-1 md:px-0 md:hidden">-</span>
<span><%= query_params[:created_at_to].present? ? l(Date.parse(query_params[:created_at_to]), locale: current_account.locale) : t('today') %></span> <span><%= query_params[:created_at_to].present? ? l(Date.parse(query_params[:created_at_to]), locale: current_account.locale) : t('today') %></span>
<% end %> <% end %>
</span> </span>
@ -77,60 +70,3 @@
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<% end %>
<% if with_status_button && chips_html.blank? %>
<div class="dropdown md:hidden shrink-0">
<label tabindex="0" class="cursor-pointer flex h-10 px-2 py-1 space-x-1 text-base items-center border text-neutral rounded-xl <%= current_status == 'all' ? 'border-neutral-300' : 'border-neutral-700' %>">
<%= svg_icon(status_icons[current_status], class: 'w-5 h-5 shrink-0') %>
<span><%= t(current_status) %></span>
<%= svg_icon('chevron_down', class: 'w-4 h-4 shrink-0') %>
</label>
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box" style="min-width: 180px;">
<li>
<%= link_to url_for(params: request.query_parameters.except('status')), class: ('bg-base-200' if current_status == 'all') do %>
<%= svg_icon('list', class: 'w-5 h-5 shrink-0') %>
<span><%= t('all') %></span>
<% if local_assigns[:all_count] %>
<span class="badge badge-sm badge-ghost font-medium px-2 ml-auto">
<%= local_assigns[:all_count] %>
</span>
<% end %>
<% end %>
</li>
<li>
<%= link_to url_for(params: request.query_parameters.merge('status' => 'pending')), class: ('bg-base-200' if current_status == 'pending') do %>
<%= svg_icon('clock', class: 'w-5 h-5 shrink-0') %>
<span><%= t('pending') %></span>
<% if local_assigns[:pending_count] %>
<span class="badge badge-sm badge-ghost font-medium px-2 ml-auto">
<%= local_assigns[:pending_count] %>
</span>
<% end %>
<% end %>
</li>
<li>
<%= link_to url_for(params: request.query_parameters.merge('status' => 'completed')), class: ('bg-base-200' if current_status == 'completed') do %>
<%= svg_icon('circle_check', class: 'w-5 h-5 shrink-0') %>
<span><%= t('completed') %></span>
<% if local_assigns[:completed_count] %>
<span class="badge badge-sm badge-ghost font-medium px-2 ml-auto">
<%= local_assigns[:completed_count] %>
</span>
<% end %>
<% end %>
</li>
</ul>
</div>
<% end %>
<% if chips_html.present? %>
<scroll-fade class="flex items-center gap-2 overflow-x-auto flex-1 min-w-0 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&::-webkit-scrollbar]:h-0 [&::-webkit-scrollbar]:w-0 md:contents">
<% if with_status_button %>
<a href="<%= submissions_filter_path('status', query_params.merge(path: url_for)) %>" data-turbo-frame="modal" class="order-none md:hidden flex h-10 px-2 py-1 space-x-1 text-base items-center border text-neutral rounded-xl shrink-0 <%= current_status == 'all' ? 'border-neutral-300' : 'border-neutral-700' %>">
<%= svg_icon(status_icons[current_status], class: 'w-5 h-5 shrink-0') %>
<span><%= t(current_status) %></span>
<%= svg_icon('chevron_down', class: 'w-4 h-4 shrink-0') %>
</a>
<% end %>
<%= chips_html %>
</scroll-fade>
<% end %>

@ -1,13 +1,10 @@
<% query_params = params.permit(:q).merge(filter_params) %> <% query_params = params.permit(:q).merge(filter_params) %>
<% advanced_filters = filter_params.then { |f| f[:status].in?(%w[pending completed]) ? f.except(:status) : f } %> <div class="dropdown dropdown-end">
<div class="dropdown dropdown-end shrink-0"> <label tabindex="0" class="cursor-pointer flex h-10 px-3 py-1 space-x-1 text-lg items-center justify-between border text-center text-neutral rounded-xl border-neutral-300 hover:border-neutral-700">
<label tabindex="0" class="cursor-pointer flex h-10 py-1 text-lg items-center border text-center text-neutral rounded-xl hover:border-neutral-700 <%= advanced_filters.present? ? 'w-10 md:w-auto px-0 md:px-3 space-x-0 md:space-x-1 justify-center md:justify-between border-neutral-700 md:border-neutral-300' : 'px-3 space-x-1 justify-between border-neutral-300' %>">
<%= svg_icon('filter', class: 'w-5 h-5 flex-shrink-0 stroke-2') %> <%= svg_icon('filter', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
<% if advanced_filters.blank? %> <span class="<%= filter_params.then { |f| f[:status].in?(%w[pending completed]) ? f.except(:status) : f }.present? ? 'md:hidden' : '' %>">Filter</span>
<span>Filter</span>
<% end %>
</label> </label>
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box text-right" style="min-width: 180px;"> <ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box min-w-[180px] text-right">
<li class="flex"> <li class="flex">
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %> <%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
<%= svg_icon('calendar_check', class: 'w-5 h-5 flex-shrink-0 stroke-2') %> <%= svg_icon('calendar_check', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>

@ -3,9 +3,9 @@
<div class="form-control w-full"> <div class="form-control w-full">
<div id="status" class="radio-select grid grid-cols-2 gap-2 px-1"> <div id="status" class="radio-select grid grid-cols-2 gap-2 px-1">
<% ['', 'pending', 'completed', 'partially_completed', 'sent', 'opened', 'declined', 'expired'].each do |status| %> <% ['', 'pending', 'completed', 'partially_completed', 'sent', 'opened', 'declined', 'expired'].each do |status| %>
<label class="radio-label cursor-pointer inline-flex items-center space-x-2 min-w-0"> <label class="radio-label cursor-pointer inline-flex items-center space-x-2">
<%= radio_button_tag 'status', status, params[:status] == status || (status == '' && params[:status].blank?), class: 'base-radio shrink-0' %> <%= radio_button_tag 'status', status, params[:status] == status || (status == '' && params[:status].blank?), class: 'base-radio' %>
<span class="truncate"><%= t(status.presence || 'all') %></span> <span><%= t(status.presence || 'all') %></span>
</label> </label>
<% end %> <% end %>
</div> </div>

@ -33,7 +33,7 @@
<%= form_for '', url: submit_form_email_2fa_path, method: :post, html: { class: 'space-y-4', id: 'code_form' } do |f| %> <%= form_for '', url: submit_form_email_2fa_path, method: :post, html: { class: 'space-y-4', id: 'code_form' } do |f| %>
<div dir="auto" class="form-control !mt-0"> <div dir="auto" class="form-control !mt-0">
<%= f.hidden_field :submitter_slug, value: @submitter.slug %> <%= f.hidden_field :submitter_slug, value: @submitter.slug %>
<%= f.text_field :one_time_code, required: true, inputmode: 'numeric', class: 'base-input text-center', placeholder: 'XXXXXX' %> <%= f.text_field :one_time_code, required: true, class: 'base-input text-center', placeholder: 'XXX-XXX' %>
<div class="flex justify-between items-center mt-1"> <div class="flex justify-between items-center mt-1">
<span> <span>
<% if flash[:alert] %> <% if flash[:alert] %>

@ -37,12 +37,10 @@
<span class="hidden sm:block"> <span class="hidden sm:block">
<%= render 'templates/upload_button', folder_name: @template_folder.full_name %> <%= render 'templates/upload_button', folder_name: @template_folder.full_name %>
</span> </span>
<open-modal-mobile data-disabled="<%= !Docuseal.multitenant? %>" class="flex">
<%= link_to new_template_path(folder_name: @template_folder.full_name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %> <%= link_to new_template_path(folder_name: @template_folder.full_name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:block"><%= t('create') %></span> <span class="hidden md:block"><%= t('create') %></span>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
</div> </div>
</div> </div>

@ -1,5 +1,5 @@
<% status_badges = { 'awaiting' => 'badge-info', 'sent' => 'badge-info', 'completed' => 'badge-success', 'opened' => 'badge-warning', 'declined' => 'badge-error' } %> <% status_badges = { 'awaiting' => 'badge-info', 'sent' => 'badge-info', 'completed' => 'badge-success', 'opened' => 'badge-warning', 'declined' => 'badge-error' } %>
<div class="bg-base-200 rounded-2xl flex flex-col sm:flex-row"> <div class="bg-base-200 rounded-2xl flex flex-col sm:flex-row items-strech">
<% if local_assigns[:with_template] %> <% if local_assigns[:with_template] %>
<% template = submission.template %> <% template = submission.template %>
<a href="<%= template ? template_path(template) : submission_path(submission) %>" class="px-5 sm:pr-3 py-3 group sm:rounded-l-2xl sm:rounded-tr-none rounded-t-2xl flex sm:flex-col justify-between sm:w-52 w-full flex-shrink-0 bg-base-300/60 space-x-2 sm:space-x-0"> <a href="<%= template ? template_path(template) : submission_path(submission) %>" class="px-5 sm:pr-3 py-3 group sm:rounded-l-2xl sm:rounded-tr-none rounded-t-2xl flex sm:flex-col justify-between sm:w-52 w-full flex-shrink-0 bg-base-300/60 space-x-2 sm:space-x-0">
@ -52,12 +52,10 @@
<%= submitter.name || submitter.email || submitter.phone %> <%= submitter.name || submitter.email || submitter.phone %>
</a> </a>
<% if !submitter.completed_at? && !submission.completed_at? && can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %> <% if !submitter.completed_at? && !submission.completed_at? && can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %>
<span class="pl-0.5 tooltip tooltip-top tooltip-no-touch md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>"> <span class="pl-0.5 tooltip tooltip-top md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>">
<open-modal-mobile>
<%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %> <%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('pencil', class: 'w-5 h-5') %> <%= svg_icon('pencil', class: 'w-5 h-5') %>
<% end %> <% end %>
</open-modal-mobile>
</span> </span>
<% end %> <% end %>
</span> </span>
@ -104,12 +102,12 @@
</a> </a>
</div> </div>
<% if !submission.archived_at? && !template&.archived_at? && can?(:destroy, submission) %> <% if !submission.archived_at? && !template&.archived_at? && can?(:destroy, submission) %>
<span data-tip="<%= t('archive') %>" class="tooltip tooltip-top tooltip-no-touch"> <span data-tip="<%= t('archive') %>" class="sm:tooltip tooltip-top">
<%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('archive', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('archive'), method: :delete %> <%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('archive', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('archive'), method: :delete %>
</span> </span>
<% end %> <% end %>
<% if local_assigns[:archived] && can?(:destroy, submission) %> <% if local_assigns[:archived] && can?(:destroy, submission) %>
<span data-tip="<%= t('remove') %>" class="tooltip tooltip-top tooltip-no-touch"> <span data-tip="<%= t('remove') %>" class="sm:tooltip tooltip-top">
<%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('trash', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission, permanently: true), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('remove'), method: :delete, data: { turbo_confirm: t('submission_deletion_is_irreversible_and_will_permanently_remove_all_associated_signed_documents_with_it_are_you_sure_') } %> <%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('trash', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission, permanently: true), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('remove'), method: :delete, data: { turbo_confirm: t('submission_deletion_is_irreversible_and_will_permanently_remove_all_associated_signed_documents_with_it_are_you_sure_') } %>
</span> </span>
<% end %> <% end %>
@ -119,7 +117,7 @@
<div class="flex flex-col md:flex-row md:items-center gap-3"> <div class="flex flex-col md:flex-row md:items-center gap-3">
<% if is_submission_completed %> <% if is_submission_completed %>
<% latest_submitter = submitters.select(&:completed_at?).max_by(&:completed_at) %> <% latest_submitter = submitters.select(&:completed_at?).max_by(&:completed_at) %>
<a href="<%= submission_path(submission) %>" class="z-[1] tooltip tooltip-no-touch flex" data-tip="<%= l(latest_submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>"> <a href="<%= submission_path(submission) %>" class="z-[1] tooltip flex" data-tip="<%= l(latest_submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge <%= status_badges[latest_submitter.status] %> md:w-32 bg-opacity-50 badge-lg uppercase text-sm font-semibold"> <span class="badge <%= status_badges[latest_submitter.status] %> md:w-32 bg-opacity-50 badge-lg uppercase text-sm font-semibold">
<%= t(latest_submitter.status) %> <%= t(latest_submitter.status) %>
</span> </span>
@ -136,7 +134,7 @@
<div class="relative flex justify-between items-start md:items-center space-x-3"> <div class="relative flex justify-between items-start md:items-center space-x-3">
<span class="flex flex-col md:flex-row md:items-center gap-2"> <span class="flex flex-col md:flex-row md:items-center gap-2">
<% if !is_submission_completed && !submission.expired? %> <% if !is_submission_completed && !submission.expired? %>
<a href="<%= submission_path(submission) %>" class="z-[1] tooltip tooltip-no-touch flex" data-tip="<%= l(submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>"> <a href="<%= submission_path(submission) %>" class="z-[1] tooltip flex" data-tip="<%= l(submitter.status_event_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %>">
<span class="badge md:w-24 <%= status_badges[submitter.status] %> bg-opacity-50 uppercase text-xs font-semibold"> <span class="badge md:w-24 <%= status_badges[submitter.status] %> bg-opacity-50 uppercase text-xs font-semibold">
<%= t(submitter.status) %> <%= t(submitter.status) %>
</span> </span>
@ -147,12 +145,10 @@
<%= submitter.name || submitter.email || submitter.phone %> <%= submitter.name || submitter.email || submitter.phone %>
</a> </a>
<% if !submitter.completed_at? && !submission.completed_at? && can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %> <% if !submitter.completed_at? && !submission.completed_at? && can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %>
<span class="pl-0.5 tooltip tooltip-top tooltip-no-touch md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>"> <span class="pl-0.5 tooltip tooltip-top md:opacity-0 md:hover:opacity-100 md:peer-hover:opacity-100" data-tip="<%= t('edit') %>">
<open-modal-mobile>
<%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %> <%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('pencil', class: 'w-5 h-5') %> <%= svg_icon('pencil', class: 'w-5 h-5') %>
<% end %> <% end %>
</open-modal-mobile>
</span> </span>
<% end %> <% end %>
</span> </span>
@ -213,12 +209,12 @@
</a> </a>
</div> </div>
<% if !submission.archived_at? && !template&.archived_at? %> <% if !submission.archived_at? && !template&.archived_at? %>
<span data-tip="<%= t('archive') %>" class="tooltip tooltip-top tooltip-no-touch"> <span data-tip="<%= t('archive') %>" class="sm:tooltip tooltip-top">
<%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('archive', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('archive'), method: :delete %> <%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('archive', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('archive'), method: :delete %>
</span> </span>
<% end %> <% end %>
<% if local_assigns[:archived] && can?(:destroy, submission) %> <% if local_assigns[:archived] && can?(:destroy, submission) %>
<span data-tip="<%= t('remove') %>" class="tooltip tooltip-top tooltip-no-touch"> <span data-tip="<%= t('remove') %>" class="sm:tooltip tooltip-top">
<%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('trash', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission, permanently: true), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('remove'), method: :delete, data: { turbo_confirm: t('submission_deletion_is_irreversible_and_will_permanently_remove_all_associated_signed_documents_with_it_are_you_sure_') } %> <%= button_to button_title(title: nil, disabled_with: nil, icon: svg_icon('trash', class: 'w-6 h-6'), icon_disabled: svg_icon('loader', class: 'w-6 h-6 animate-spin')), submission_path(submission, permanently: true), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('remove'), method: :delete, data: { turbo_confirm: t('submission_deletion_is_irreversible_and_will_permanently_remove_all_associated_signed_documents_with_it_are_you_sure_') } %>
</span> </span>
<% end %> <% end %>

@ -15,12 +15,12 @@
<% end %> <% end %>
</p> </p>
<p class="flex text-xs text-base-content/60"> <p class="flex text-xs text-base-content/60">
<span class="flex items-center space-x-1 w-1/2 md:w-2/3 lg:w-1/2 whitespace-nowrap"> <span class="flex items-center space-x-1 w-1/2">
<%= svg_icon('calendar', class: 'w-4 h-4 flex-shrink-0') %> <%= svg_icon('calendar', class: 'w-4 h-4') %>
<span><%= l(template.created_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %></span> <span><%= l(template.created_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) %></span>
</span> </span>
<% if local_assigns[:with_folder] %> <% if local_assigns[:with_folder] %>
<span class="flex items-center space-x-1 w-1/2 md:w-1/3 lg:w-1/2"> <span class="flex items-center space-x-1 w-1/2">
<%= svg_icon('folder', class: 'w-4 h-4 flex-shrink-0') %> <%= svg_icon('folder', class: 'w-4 h-4 flex-shrink-0') %>
<span class="truncate"><%= template.folder.full_name %></span> <span class="truncate"><%= template.folder.full_name %></span>
</span> </span>
@ -31,36 +31,36 @@
<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="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 tooltip-no-touch" data-tip="<%= t('move') %>"> <span class="tooltip tooltip-left" data-tip="<%= t('move') %>">
<a href="<%= edit_template_folder_path(template.id) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle"> <a href="<%= edit_template_folder_path(template.id) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
<%= svg_icon('folder_share', class: 'w-4 h-4') %> <%= svg_icon('folder_share', class: 'w-4 h-4') %>
</a> </a>
</span> </span>
<% end %> <% end %>
<% if template.archived_at? && can?(:destroy, template) %> <% if template.archived_at? && can?(:destroy, template) %>
<span class="tooltip tooltip-left tooltip-no-touch" data-tip="<%= t('restore') %>"> <span class="tooltip tooltip-left" data-tip="<%= t('restore') %>">
<%= button_to template_restore_index_path(template), class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle' do %> <%= button_to template_restore_index_path(template), class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle' do %>
<%= svg_icon('rotate', class: 'w-4 h-4 enabled') %> <%= svg_icon('rotate', class: 'w-4 h-4 enabled') %>
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %> <%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
<% end %> <% end %>
</span> </span>
<% elsif !template.archived_at? && can?(:update, template) %> <% elsif !template.archived_at? && can?(:update, template) %>
<span class="tooltip tooltip-left tooltip-no-touch" data-tip="<%= t('edit') %>"> <span class="tooltip tooltip-left" data-tip="<%= t('edit') %>">
<a href="<%= edit_template_path(template) %>" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle"> <a href="<%= edit_template_path(template) %>" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
<%= svg_icon('pencil', class: 'w-4 h-4') %> <%= svg_icon('pencil', class: 'w-4 h-4') %>
</a> </a>
</span> </span>
<% end %> <% end %>
<% if can?(:create, template) %> <% if can?(:create, template) %>
<span class="tooltip tooltip-left tooltip-no-touch" data-tip="<%= t('clone') %>"> <span class="tooltip tooltip-left" data-tip="<%= t('clone') %>">
<a href="<%= new_template_clone_path(template) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle"> <a href="<%= new_template_clone_path(template) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
<%= svg_icon('copy', class: 'w-4 h-4') %> <%= svg_icon('copy', class: 'w-4 h-4') %>
</a> </a>
</span> </span>
<% end %> <% end %>
<% if can?(:destroy, template) %> <% if can?(:destroy, template) %>
<span class="tooltip tooltip-left tooltip-no-touch" data-tip="<%= template.archived_at? ? t('delete') : t('archive') %>"> <span class="tooltip tooltip-left" data-tip="<%= template.archived_at? ? t('delete') : t('archive') %>">
<%= button_to template_path(template), data: template.archived_at? ? { turbo_confirm: t('template_deletion_is_irreversible_and_will_permanently_remove_all_associated_signed_documents_with_it_are_you_sure_') } : {}, params: { permanently: template.archived_at? }.compact_blank, method: :delete, class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle', aria: { label: template.archived_at? ? t('delete') : t('archive') } do %> <%= button_to template_path(template), data: template.archived_at? ? { turbo_confirm: t('template_deletion_is_irreversible_and_will_permanently_remove_all_associated_signed_documents_with_it_are_you_sure_') } : {}, params: { permanently: template.archived_at? }.compact_blank, method: :delete, class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle', aria_label: t('restore') do %>
<%= svg_icon(template.archived_at? ? 'trash' : 'archive', class: 'w-4 h-4 enabled') %> <%= svg_icon(template.archived_at? ? 'trash' : 'archive', class: 'w-4 h-4 enabled') %>
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %> <%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
<% end %> <% end %>

@ -20,7 +20,7 @@
</span> </span>
</a> </a>
<% if can?(:update, template) %> <% if can?(:update, template) %>
<span class="pl-1 tooltip tooltip-right tooltip-no-touch md:opacity-0 hover:opacity-100 peer-hover:opacity-100" data-tip="<%= t('move') %>"> <span class="pl-1 tooltip tooltip-right md:opacity-0 hover:opacity-100 peer-hover:opacity-100" data-tip="<%= t('move') %>">
<a href="<%= edit_template_folder_path(template.id, subfolder: @template.folder.parent_folder.present?) %>" data-turbo-frame="modal"> <a href="<%= edit_template_folder_path(template.id, subfolder: @template.folder.parent_folder.present?) %>" data-turbo-frame="modal">
<%= svg_icon('pencil_share', class: 'w-5 h-5') %> <%= svg_icon('pencil_share', class: 'w-5 h-5') %>
</a> </a>
@ -44,7 +44,7 @@
<% if !template.archived_at? %> <% if !template.archived_at? %>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<% if can?(:update, template) %> <% if can?(:update, template) %>
<div class="tooltip tooltip-no-touch hidden md:block" data-tip="<%= t('preferences') %>"> <div class="tooltip" data-tip="<%= t('preferences') %>">
<%= link_to template_preferences_path(template), class: 'btn border border-base-200 bg-base-200 hover:bg-base-300 hover:border-base-300 btn-sm flex-1 hidden md:flex', data: { turbo_frame: :modal } do %> <%= link_to template_preferences_path(template), class: 'btn border border-base-200 bg-base-200 hover:bg-base-300 hover:border-base-300 btn-sm flex-1 hidden md:flex', data: { turbo_frame: :modal } do %>
<span class="flex items-center justify-center space-x-2"> <span class="flex items-center justify-center space-x-2">
<%= svg_icon('adjustments_horizontal', class: 'w-6 h-6') %> <%= svg_icon('adjustments_horizontal', class: 'w-6 h-6') %>
@ -52,14 +52,12 @@
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<open-modal-mobile class="absolute md:relative bottom-0 right-0 mt-1"> <%= link_to template_share_link_path(template), class: 'absolute md:relative bottom-0 right-0 btn btn-xs md:btn-sm whitespace-nowrap btn-neutral text-white mt-1 px-2', data: { turbo_frame: :modal } do %>
<%= link_to template_share_link_path(template), class: 'btn btn-xs md:btn-sm whitespace-nowrap btn-neutral text-white px-2', data: { turbo_frame: :modal } do %>
<span class="flex items-center justify-center space-x-2"> <span class="flex items-center justify-center space-x-2">
<%= svg_icon('link', class: 'w-4 h-4 md:w-6 md:h-6') %> <%= svg_icon('link', class: 'w-4 h-4 md:w-6 md:h-6') %>
<span><%= t('link') %></span> <span><%= t('link') %></span>
</span> </span>
<% end %> <% end %>
</open-modal-mobile>
</div> </div>
<% end %> <% end %>
</div> </div>
@ -68,48 +66,40 @@
<%= button_to button_title(title: t('archive'), disabled_with: t('archiving')[..-4], title_class: 'inline', icon: svg_icon('archive', class: 'w-6 h-6')), template_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1', method: :delete %> <%= button_to button_title(title: t('archive'), disabled_with: t('archiving')[..-4], title_class: 'inline', icon: svg_icon('archive', class: 'w-6 h-6')), template_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1', method: :delete %>
<% end %> <% end %>
<% if can?(:create, current_account.templates.new(author: current_user)) %> <% if can?(:create, current_account.templates.new(author: current_user)) %>
<div class="flex-1"> <%= link_to new_template_clone_path(template), class: 'btn btn-outline btn-sm flex-1', data: { turbo_frame: :modal } do %>
<%= link_to new_template_clone_path(template), class: 'btn btn-outline btn-sm w-full', data: { turbo_frame: :modal } do %>
<span class="flex items-center justify-center space-x-2"> <span class="flex items-center justify-center space-x-2">
<%= svg_icon('copy', class: 'w-6 h-6') %> <%= svg_icon('copy', class: 'w-6 h-6') %>
<span class="inline"><%= t('clone') %></span> <span class="inline"><%= t('clone') %></span>
</span> </span>
<% end %> <% end %>
</div>
<% end %> <% end %>
<% if !template.archived_at? %> <% if !template.archived_at? %>
<% if can?(:update, template) %> <% if can?(:update, template) %>
<div class="flex-1"> <%= link_to edit_template_path(template), class: 'btn btn-outline btn-sm flex-1' do %>
<%= link_to edit_template_path(template), class: 'btn btn-outline btn-sm w-full' do %>
<span class="flex items-center justify-center space-x-2"> <span class="flex items-center justify-center space-x-2">
<%= svg_icon('pencil', class: 'w-6 h-6') %> <%= svg_icon('pencil', class: 'w-6 h-6') %>
<span><%= t('edit') %></span> <span><%= t('edit') %></span>
</span> </span>
<% end %> <% end %>
</div>
<% elsif can?(:read, template) && !template.archived_at? %> <% elsif can?(:read, template) && !template.archived_at? %>
<div class="flex-1"> <%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm flex-1' do %>
<%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm w-full' do %>
<span class="flex items-center justify-center space-x-2"> <span class="flex items-center justify-center space-x-2">
<%= svg_icon('file_text', class: 'w-6 h-6') %> <%= svg_icon('file_text', class: 'w-6 h-6') %>
<span><%= t('preview') %></span> <span><%= t('preview') %></span>
</span> </span>
<% end %> <% end %>
</div>
<% end %> <% end %>
<% end %> <% end %>
<% if template.archived_at? %> <% if template.archived_at? %>
<% if can?(:destroy, template) %> <% if can?(:destroy, template) %>
<%= button_to button_title(title: t('restore'), disabled_with: t('restoring')[..-4], icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1' %> <%= button_to button_title(title: t('restore'), disabled_with: t('restoring')[..-4], icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm flex-1' %>
<% end %> <% end %>
<div class="flex-1"> <%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm flex-1' do %>
<%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm w-full' do %>
<span class="flex items-center justify-center space-x-2"> <span class="flex items-center justify-center space-x-2">
<%= svg_icon('file_text', class: 'w-6 h-6') %> <%= svg_icon('file_text', class: 'w-6 h-6') %>
<span><%= t('preview') %></span> <span><%= t('preview') %></span>
</span> </span>
<% end %> <% end %>
</div>
<% end %> <% end %>
</div> </div>
</div> </div>

@ -18,12 +18,10 @@
<span><%= t('export') %></span> <span><%= t('export') %></span>
<% end %> <% end %>
<% if !@template.archived_at? && can?(:create, Submission) %> <% if !@template.archived_at? && can?(:create, Submission) %>
<open-modal-mobile class="flex">
<%= link_to new_template_submission_path(@template), id: 'add_recipients_button', class: 'white-button !border', data: { turbo_frame: 'modal' } do %> <%= link_to new_template_submission_path(@template), id: 'add_recipients_button', class: 'white-button !border', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<%= t('add_recipients_html') %> <%= t('add_recipients_html') %>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
</div> </div>
</div> </div>
@ -31,7 +29,7 @@
<% end %> <% end %>
<% if is_show_tabs %> <% if is_show_tabs %>
<div class="flex items-center flex-col md:flex-row md:flex-wrap gap-2 mb-4"> <div class="flex items-center flex-col md:flex-row md:flex-wrap gap-2 mb-4">
<div class="hidden md:flex md:items-end gap-2 md:w-fit"> <div class="flex items-center md:items-end flex-col md:flex-row gap-2 w-full md:w-fit">
<a href="<%= url_for(params: request.query_parameters.except('status')) %>" class="<%= params[:status].blank? ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-700"> <a href="<%= url_for(params: request.query_parameters.except('status')) %>" class="<%= params[:status].blank? ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-700">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<%= svg_icon('list', class: 'w-5 h-5') %> <%= svg_icon('list', class: 'w-5 h-5') %>
@ -39,7 +37,7 @@
</div> </div>
<% unless can?(:manage, :countless) %> <% unless can?(:manage, :countless) %>
<div class="badge badge-neutral badge-outline font-medium"> <div class="badge badge-neutral badge-outline font-medium">
<%= all_count = params[:status].blank? && filter_params.blank? ? @pagy.count : @base_submissions.count %> <%= params[:status].blank? && filter_params.blank? ? @pagy.count : @base_submissions.count %>
</div> </div>
<% end %> <% end %>
</a> </a>
@ -50,7 +48,7 @@
</div> </div>
<% unless can?(:manage, :countless) %> <% unless can?(:manage, :countless) %>
<div class="badge badge-neutral badge-outline font-medium"> <div class="badge badge-neutral badge-outline font-medium">
<%= pending_count = params[:status] == 'pending' && filter_params.blank? ? @pagy.count : @base_submissions.pending.count %> <%= params[:status] == 'pending' && filter_params.blank? ? @pagy.count : @base_submissions.pending.count %>
</div> </div>
<% end %> <% end %>
</a> </a>
@ -61,13 +59,13 @@
</div> </div>
<% unless can?(:manage, :countless) %> <% unless can?(:manage, :countless) %>
<div class="badge badge-neutral badge-outline font-medium"> <div class="badge badge-neutral badge-outline font-medium">
<%= completed_count = params[:status] == 'completed' && filter_params.blank? ? @pagy.count : @base_submissions.completed.count %> <%= params[:status] == 'completed' && filter_params.blank? ? @pagy.count : @base_submissions.completed.count %>
</div> </div>
<% end %> <% end %>
</a> </a>
</div> </div>
<div class="flex items-center md:items-end 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:, with_status: true, all_count:, pending_count:, completed_count: %> <%= render 'submissions_filters/applied_filters', filter_params: %>
<%= render 'submissions_filters/filter_button', filter_params: %> <%= render 'submissions_filters/filter_button', filter_params: %>
<% unless @template.archived_at? %> <% unless @template.archived_at? %>
<%= render 'submissions_filters/actions', filter_params: filter_params.merge(template_id: @template.id) %> <%= render 'submissions_filters/actions', filter_params: filter_params.merge(template_id: @template.id) %>
@ -91,12 +89,10 @@
<p><%= t('send_an_invitation_to_fill_and_complete_the_form') %></p> <p><%= t('send_an_invitation_to_fill_and_complete_the_form') %></p>
<div class="space-y-2 flex flex-col"> <div class="space-y-2 flex flex-col">
<% if can?(:create, Submission) %> <% if can?(:create, Submission) %>
<open-modal-mobile class="flex flex-col mt-6"> <%= link_to new_template_submission_path(@template, with_link: true), id: 'send_to_recipients_button', class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %>
<%= link_to new_template_submission_path(@template, with_link: true), id: 'send_to_recipients_button', class: 'base-button', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="mr-1"><%= t('send_to_recipients') %></span> <span class="mr-1"><%= t('send_to_recipients') %></span>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
<% if Templates.filter_undefined_submitters(@template.submitters).size == 1 && @template.variables_schema.blank? %> <% if Templates.filter_undefined_submitters(@template.submitters).size == 1 && @template.variables_schema.blank? %>
<%= button_to start_form_path(@template.slug), params: { selfsign: true }, method: :put, class: 'white-button w-full', form: { style: 'display: inline', target: '_blank', data: { turbo: false } } do %> <%= button_to start_form_path(@template.slug), params: { selfsign: true }, method: :put, class: 'white-button w-full', form: { style: 'display: inline', target: '_blank', data: { turbo: false } } do %>
@ -104,12 +100,10 @@
<span class="mr-1"><%= t('sign_it_yourself') %></span> <span class="mr-1"><%= t('sign_it_yourself') %></span>
<% end %> <% end %>
<% else %> <% else %>
<open-modal-mobile class="flex flex-col mt-6"> <%= link_to new_template_submission_path(@template, selfsign: true), id: 'sign_yourself_button', class: 'white-button mt-6', data: { turbo_frame: 'modal' } do %>
<%= link_to new_template_submission_path(@template, selfsign: true), id: 'sign_yourself_button', class: 'white-button', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('writing', class: 'w-6 h-6') %> <%= svg_icon('writing', class: 'w-6 h-6') %>
<span class="mr-1"><%= t('sign_it_yourself') %></span> <span class="mr-1"><%= t('sign_it_yourself') %></span>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
</div> </div>
</div> </div>

@ -18,12 +18,12 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="flex flex-nowrap md:flex-wrap md:justify-end items-center gap-2"> <div class="flex flex-wrap justify-end items-center gap-2">
<%= render 'submissions_filters/applied_filters', filter_params:, with_status: true %> <%= render 'submissions_filters/applied_filters', filter_params: %>
<div class="hidden md:block shrink-0 md:order-first"> <div class="shrink-0 md:order-first">
<%= link_to new_template_submissions_export_path(@template, archived: true), class: 'btn btn-ghost text-base h-10 min-h-10', data: { turbo_frame: 'modal' } do %> <%= link_to new_template_submissions_export_path(@template, archived: true), class: 'btn btn-ghost text-base h-10 min-h-10', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('download', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('download', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:inline"><%= t('export') %></span> <span><%= t('export') %></span>
<% end %> <% end %>
</div> </div>
<%= render 'submissions_filters/filter_button', filter_params: %> <%= render 'submissions_filters/filter_button', filter_params: %>

@ -30,12 +30,10 @@
<span class="hidden sm:block"> <span class="hidden sm:block">
<%= render 'templates/upload_button' %> <%= render 'templates/upload_button' %>
</span> </span>
<open-modal-mobile data-disabled="<%= !Docuseal.multitenant? %>" class="flex">
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %> <%= 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') %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:block"><%= t('create') %></span> <span class="hidden md:block"><%= t('create') %></span>
<% end %> <% end %>
</open-modal-mobile>
<% end %> <% end %>
</div> </div>
</div> </div>

@ -10,7 +10,9 @@
<span class="flex items-center mt-1"> <span class="flex items-center mt-1">
<%= svg_icon('folder', class: 'w-5 h-5 flex-shrink-0 group-hover:hidden mr-1') %> <%= svg_icon('folder', class: 'w-5 h-5 flex-shrink-0 group-hover:hidden mr-1') %>
<%= svg_icon('folder_share', class: 'w-5 h-5 flex-shrink-0 hidden group-hover:inline mr-1') %> <%= svg_icon('folder_share', class: 'w-5 h-5 flex-shrink-0 hidden group-hover:inline mr-1') %>
<span><%= @template.folder.parent_folder&.name || @template.folder.name %></span> <span class="text-md">
<%= @template.folder.parent_folder&.name || @template.folder.name %>
</span>
</span> </span>
</label> </label>
</div> </div>

@ -3,14 +3,14 @@
<% show_recipients = @template.submitters.to_a.length > 1 %> <% show_recipients = @template.submitters.to_a.length > 1 %>
<% options = [[t('general'), 'general']] %> <% options = [[t('general'), 'general']] %>
<% options << [t('recipients'), 'recipients'] if show_recipients %> <% options << [t('recipients'), 'recipients'] if show_recipients %>
<% options << [safe_join([tag.span('API', class: 'md:hidden'), tag.span(t('api_and_embedding'), class: 'hidden md:inline')]), 'api'] if show_api %> <% options << [t('api_and_embedding'), 'api'] if show_api %>
<% if options.size > 1 %> <% if options.size > 1 %>
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center mt-3 block"> <toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center mt-3 block">
<div class="join"> <div class="join">
<% options.each_with_index do |(label, value), index| %> <% options.each_with_index do |(label, value), index| %>
<span> <span>
<%= radio_button_tag 'option', value, value == 'general', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> <%= radio_button_tag 'option', value, value == 'general', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %>
<label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-sm join-item peer-checked:btn-active normal-case px-8 md:px-0 <%= 'min-w-28 md:min-w-0' if value == 'api' %> <%= options.size > 2 ? 'md:w-44' : 'md:w-48' %>"> <label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-focus btn-sm join-item peer-checked:btn-active normal-case <%= 'px-8 md:px-0' if value.in?(%w[general recipients]) %> <%= options.size > 2 ? 'md:w-44' : 'md:w-48' %>">
<%= label %> <%= label %>
</label> </label>
</span> </span>

@ -26,7 +26,7 @@
<div class="relative flex-grow"> <div class="relative flex-grow">
<input id="embedding_url" type="text" value="<%= start_form_url(slug: @template.slug, host: form_link_host) %>" class="base-input w-full pr-10" autocomplete="off" readonly> <input id="embedding_url" type="text" value="<%= start_form_url(slug: @template.slug, host: form_link_host) %>" class="base-input w-full pr-10" autocomplete="off" readonly>
<% if @template.variables_schema.blank? && !@template.preferences&.dig('require_email_2fa') && !@template.preferences&.dig('require_phone_2fa') %> <% if @template.variables_schema.blank? && !@template.preferences&.dig('require_email_2fa') && !@template.preferences&.dig('require_phone_2fa') %>
<a href="<%= template_share_link_qr_path(@template) %>" target="_blank" rel="noopener" class="absolute top-1/2 -translate-y-1/2 right-2 flex items-center justify-center tooltip tooltip-no-touch text-base-content/70 hover:text-base-content bg-white rounded px-1 py-0.5" data-tip="<%= t('qr_code') %>" aria-label="<%= t('qr_code') %>"> <a href="<%= template_share_link_qr_path(@template) %>" target="_blank" rel="noopener" class="absolute top-1/2 -translate-y-1/2 right-2 flex items-center justify-center tooltip text-base-content/70 hover:text-base-content bg-white rounded px-1 py-0.5" data-tip="<%= t('qr_code') %>" aria-label="<%= t('qr_code') %>">
<%= svg_icon('qrcode', class: 'w-6 h-6') %> <%= svg_icon('qrcode', class: 'w-6 h-6') %>
</a> </a>
<% end %> <% end %>

@ -5,7 +5,7 @@
<% options.each_with_index do |(label, value), index| %> <% options.each_with_index do |(label, value), index| %>
<span> <span>
<%= radio_button_tag 'option', value, value == 'draw', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> <%= radio_button_tag 'option', value, value == 'draw', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %>
<label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-sm join-item md:w-28 peer-checked:btn-active normal-case"> <label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-focus btn-sm join-item md:w-28 peer-checked:btn-active normal-case">
<%= label %> <%= label %>
</label> </label>
</span> </span>

@ -5,7 +5,7 @@
<% options.each_with_index do |(label, value), index| %> <% options.each_with_index do |(label, value), index| %>
<span> <span>
<%= radio_button_tag 'option', value, value == 'draw', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> <%= radio_button_tag 'option', value, value == 'draw', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %>
<label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-sm join-item md:w-28 peer-checked:btn-active normal-case"> <label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-focus btn-sm join-item md:w-28 peer-checked:btn-active normal-case">
<%= label %> <%= label %>
</label> </label>
</span> </span>

@ -2,7 +2,7 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="md:flex-grow"> <div class="md:flex-grow">
<div class="flex flex-col md:flex-row md:flex-wrap gap-2 md:justify-between md:items-end mb-4 min-h-12"> <div class="flex flex-col md:flex-row md:flex-wrap gap-2 md:justify-between md:items-end mb-4 min-h-12">
<h1 class="text-3xl md:text-4xl font-bold"> <h1 class="text-4xl font-bold">
<% if params[:status] == 'archived' %> <% if params[:status] == 'archived' %>
<%= t('archived_users') %> <%= t('archived_users') %>
<% elsif params[:status] == 'integration' %> <% elsif params[:status] == 'integration' %>
@ -56,7 +56,7 @@
</thead> </thead>
<tbody> <tbody>
<% @users.each do |user| %> <% @users.each do |user| %>
<tr> <tr scope="row">
<td> <td>
<%= user.full_name %> <%= user.full_name %>
</td> </td>

@ -6,7 +6,7 @@
<% next_attempt_at = last_attempt.created_at + (2**last_attempt.attempt).minutes %> <% next_attempt_at = last_attempt.created_at + (2**last_attempt.attempt).minutes %>
<% if webhook_event.webhook_attempts.none?(&:success?) && last_attempt.attempt <= 10 && next_attempt_at > 30.seconds.ago %> <% if webhook_event.webhook_attempts.none?(&:success?) && last_attempt.attempt <= 10 && next_attempt_at > 30.seconds.ago %>
<li class="ml-7"> <li class="ml-7">
<span class="btn btn-outline btn-xs btn-circle pointer-events-none absolute justify-center text-base-content/60 bg-base-100" style="left: -12px;"> <span class="btn btn-outline btn-xs btn-circle pointer-events-none absolute justify-center border-base-content-/60 text-base-content/60 bg-base-100" style="left: -12px;">
<%= svg_icon('clock', class: 'w-4 h-4 shrink-0') %> <%= svg_icon('clock', class: 'w-4 h-4 shrink-0') %>
</span> </span>
<p class="leading-none text-base-content/90 pt-1"> <p class="leading-none text-base-content/90 pt-1">

@ -2,7 +2,7 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow min-w-0"> <div class="flex-grow min-w-0">
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4"> <div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4">
<h1 class="text-3xl md:text-4xl font-bold">Webhooks</h1> <h1 class="text-4xl font-bold">Webhooks</h1>
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-center"> <div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-center">
<%= render 'shared/test_mode_toggle' %> <%= render 'shared/test_mode_toggle' %>
<% if @webhook_url.persisted? %> <% if @webhook_url.persisted? %>

@ -2,7 +2,7 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow"> <div class="flex-grow">
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4"> <div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4">
<h1 class="text-3xl md:text-4xl font-bold">Webhook</h1> <h1 class="text-4xl font-bold">Webhook</h1>
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-center"> <div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-center">
<% if params[:action] == 'index' && (current_user == true_user || current_account.testing?) %> <% if params[:action] == 'index' && (current_user == true_user || current_account.testing?) %>
<%= render 'shared/test_mode_toggle' %> <%= render 'shared/test_mode_toggle' %>
@ -25,7 +25,7 @@
<%= svg_icon('lock', class: 'w-4 h-4') %> <%= svg_icon('lock', class: 'w-4 h-4') %>
<span><%= t('security') %></span> <span><%= t('security') %></span>
<% end %> <% end %>
<div class="tooltip tooltip-left md:tooltip-top tooltip-no-touch" data-tip="<%= t('delete_webhook') %>"> <div class="tooltip tooltip-left md:tooltip-top" data-tip="<%= t('delete_webhook') %>">
<%= button_to settings_webhook_path(@webhook_url), class: 'btn btn-warning btn-sm', method: :delete, data: { turbo_confirm: t('are_you_sure_') } do %> <%= button_to settings_webhook_path(@webhook_url), class: 'btn btn-warning btn-sm', method: :delete, data: { turbo_confirm: t('are_you_sure_') } do %>
<span><%= t('delete') %></span> <span><%= t('delete') %></span>
<% end %> <% end %>
@ -83,7 +83,7 @@
</div> </div>
<% if @webhook_events.present? || params[:status].present? %> <% if @webhook_events.present? || params[:status].present? %>
<div class="mt-6"> <div class="mt-6">
<h2 id="log" class="text-2xl md:text-3xl font-bold"><%= t('events_log') %></h2> <h2 id="log" class="text-3xl font-bold"><%= t('events_log') %></h2>
<div class="tabs border-b mt-4"> <div class="tabs border-b mt-4">
<%= link_to t('all'), url_for(params: request.query_parameters.except('status', 'page')), style: 'margin-bottom: -1px', class: "tab h-10 text-base #{params[:status].blank? ? 'tab-active tab-bordered' : 'pb-[3px]'}" %> <%= link_to t('all'), url_for(params: request.query_parameters.except('status', 'page')), style: 'margin-bottom: -1px', class: "tab h-10 text-base #{params[:status].blank? ? 'tab-active tab-bordered' : 'pb-[3px]'}" %>
<%= link_to t('succeeded'), url_for(params: request.query_parameters.except('page').merge('status' => 'success')), style: 'margin-bottom: -1px', class: "tab h-10 text-base #{params[:status] == 'success' ? 'tab-active tab-bordered' : 'pb-[3px]'}" %> <%= link_to t('succeeded'), url_for(params: request.query_parameters.except('page').merge('status' => 'success')), style: 'margin-bottom: -1px', class: "tab h-10 text-base #{params[:status] == 'success' ? 'tab-active tab-bordered' : 'pb-[3px]'}" %>

@ -53,25 +53,6 @@ en: &en
require_all_recipients: Require all recipients require_all_recipients: Require all recipients
stripe_account_has_been_connected: Stripe account has been connected. stripe_account_has_been_connected: Stripe account has been connected.
re_connect_stripe: Re-connect Stripe re_connect_stripe: Re-connect Stripe
paypal_integration: PayPal Integration
paypal_account_has_been_connected: PayPal account has been connected.
re_connect_paypal: Re-connect PayPal
disconnect: Disconnect
failed_to_connect_paypal: Failed to connect PayPal. Please try again.
paypal_connect_not_completed: PayPal connection was not completed.
payment_receipt: Payment Receipt
amount_paid: Amount paid
transaction: Transaction
transaction_id: Transaction ID
order_id: Order ID
payment_date: Date
paid_by: Paid by
paid_to: Paid to
business: Business
country: Country
payer_id: Payer ID
merchant_id: Merchant ID
breakdown: Breakdown
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
@ -473,7 +454,7 @@ en: &en
back_to_active: Back to Active back_to_active: Back to Active
submissions: Submissions submissions: Submissions
templates: Templates templates: Templates
document_templates_html: <span class="hidden lg:inline">Document</span> Templates document_templates_html: <span class="hidden md:inline">Document</span> Templates
archived: Archived archived: Archived
search: Search search: Search
submissions_not_found: Submissions not Found submissions_not_found: Submissions not Found
@ -965,7 +946,6 @@ en: &en
undo: Undo undo: Undo
redo: Redo redo: Redo
add_variable: Add variable add_variable: Add variable
variable: Variable
enter_a_url_or_variable_name: Enter a URL or variable name enter_a_url_or_variable_name: Enter a URL or variable name
new_token: New token new_token: New token
token: Token token: Token
@ -1144,24 +1124,6 @@ es: &es
stripe_integration: Integración con Stripe stripe_integration: Integración con Stripe
stripe_account_has_been_connected: La cuenta de Stripe ha sido conectada. stripe_account_has_been_connected: La cuenta de Stripe ha sido conectada.
re_connect_stripe: Volver a conectar Stripe re_connect_stripe: Volver a conectar Stripe
paypal_integration: Integración con PayPal
paypal_account_has_been_connected: La cuenta de PayPal ha sido conectada.
re_connect_paypal: Volver a conectar PayPal
failed_to_connect_paypal: No se pudo conectar PayPal. Por favor, inténtalo de nuevo.
paypal_connect_not_completed: La conexión con PayPal no se completó.
payment_receipt: Recibo de pago
amount_paid: Importe pagado
transaction: Transacción
transaction_id: ID de transacción
order_id: ID de pedido
payment_date: Fecha
paid_by: Pagado por
paid_to: Pagado a
business: Empresa
country: País
payer_id: ID de pagador
merchant_id: ID de comerciante
breakdown: Desglose
private: Privado private: Privado
_variables: Variables _variables: Variables
your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Su dirección de correo electrónico ha sido liberada inmediatamente. Si se une a un equipo, ahora puede ser invitado al equipo de su empresa sin esperar a que finalice el período de eliminación. your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Su dirección de correo electrónico ha sido liberada inmediatamente. Si se une a un equipo, ahora puede ser invitado al equipo de su empresa sin esperar a que finalice el período de eliminación.
@ -1573,7 +1535,7 @@ es: &es
back_to_active: Volver a activo back_to_active: Volver a activo
submissions: Envíos submissions: Envíos
templates: Plantillas templates: Plantillas
document_templates_html: Plantillas <span class="hidden lg:inline">de documento</span> document_templates_html: Plantillas <span class="hidden md:inline">de documento</span>
archived: Archivado archived: Archivado
search: Buscar search: Buscar
submissions_not_found: Envíos no encontrados submissions_not_found: Envíos no encontrados
@ -2062,7 +2024,6 @@ es: &es
undo: Deshacer undo: Deshacer
redo: Rehacer redo: Rehacer
add_variable: Agregar variable add_variable: Agregar variable
variable: Variable
enter_a_url_or_variable_name: Ingrese una URL o nombre de variable enter_a_url_or_variable_name: Ingrese una URL o nombre de variable
new_token: Nuevo token new_token: Nuevo token
token: Token token: Token
@ -2241,24 +2202,6 @@ it: &it
stripe_integration: Integrazione Stripe stripe_integration: Integrazione Stripe
stripe_account_has_been_connected: L'account Stripe è stato collegato. stripe_account_has_been_connected: L'account Stripe è stato collegato.
re_connect_stripe: Ricollega Stripe re_connect_stripe: Ricollega Stripe
paypal_integration: Integrazione PayPal
paypal_account_has_been_connected: L'account PayPal è stato collegato.
re_connect_paypal: Ricollega PayPal
failed_to_connect_paypal: Impossibile collegare PayPal. Riprova.
paypal_connect_not_completed: La connessione con PayPal non è stata completata.
payment_receipt: Ricevuta di pagamento
amount_paid: Importo pagato
transaction: Transazione
transaction_id: ID transazione
order_id: ID ordine
payment_date: Data
paid_by: Pagato da
paid_to: Pagato a
business: Azienda
country: Paese
payer_id: ID pagatore
merchant_id: ID esercente
breakdown: Dettaglio
private: Privato private: Privato
_variables: Variabili _variables: Variabili
your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Il tuo indirizzo email è stato rilasciato immediatamente. Se stai per unirti a un team, ora puoi essere invitato nel team della tua azienda senza aspettare la fine del periodo di eliminazione. your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Il tuo indirizzo email è stato rilasciato immediatamente. Se stai per unirti a un team, ora puoi essere invitato nel team della tua azienda senza aspettare la fine del periodo di eliminazione.
@ -2670,7 +2613,7 @@ it: &it
back_to_active: Torna attivo back_to_active: Torna attivo
submissions: Invii submissions: Invii
templates: Modelli templates: Modelli
document_templates_html: Modelli <span class="hidden lg:inline">di documento</span> document_templates_html: Modelli <span class="hidden md:inline">di documento</span>
archived: Archiviato archived: Archiviato
search: Cerca search: Cerca
submissions_not_found: Invii non trovati submissions_not_found: Invii non trovati
@ -3159,7 +3102,6 @@ it: &it
undo: Annulla undo: Annulla
redo: Ripeti redo: Ripeti
add_variable: Aggiungi variabile add_variable: Aggiungi variabile
variable: Variabile
enter_a_url_or_variable_name: Inserisci un URL o nome variabile enter_a_url_or_variable_name: Inserisci un URL o nome variabile
new_token: Nuovo token new_token: Nuovo token
token: Token token: Token
@ -3338,24 +3280,6 @@ fr: &fr
stripe_integration: Intégration Stripe stripe_integration: Intégration Stripe
stripe_account_has_been_connected: Le compte Stripe a été connecté. stripe_account_has_been_connected: Le compte Stripe a été connecté.
re_connect_stripe: Reconnecter Stripe re_connect_stripe: Reconnecter Stripe
paypal_integration: Intégration PayPal
paypal_account_has_been_connected: Le compte PayPal a été connecté.
re_connect_paypal: Reconnecter PayPal
failed_to_connect_paypal: Échec de la connexion à PayPal. Veuillez réessayer.
paypal_connect_not_completed: La connexion à PayPal n'a pas été complétée.
payment_receipt: Reçu de paiement
amount_paid: Montant payé
transaction: Transaction
transaction_id: ID de transaction
order_id: ID de commande
payment_date: Date
paid_by: Payé par
paid_to: Payé à
business: Entreprise
country: Pays
payer_id: ID payeur
merchant_id: ID marchand
breakdown: Détail
private: Privé private: Privé
_variables: Variables _variables: Variables
your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Votre adresse e-mail a été libérée immédiatement. Si vous rejoignez une équipe, vous pouvez maintenant être invité dans l'équipe de votre entreprise sans attendre la fin de la période de suppression. your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Votre adresse e-mail a été libérée immédiatement. Si vous rejoignez une équipe, vous pouvez maintenant être invité dans l'équipe de votre entreprise sans attendre la fin de la période de suppression.
@ -3767,7 +3691,7 @@ fr: &fr
back_to_active: Retour aux actifs back_to_active: Retour aux actifs
submissions: Soumissions submissions: Soumissions
templates: Modèles templates: Modèles
document_templates_html: Modèles <span class="hidden lg:inline">de documents</span> document_templates_html: Modèles <span class="hidden md:inline">de documents</span>
archived: Archivé archived: Archivé
search: Rechercher search: Rechercher
submissions_not_found: Soumissions introuvables submissions_not_found: Soumissions introuvables
@ -4253,7 +4177,6 @@ fr: &fr
undo: Annuler undo: Annuler
redo: Rétablir redo: Rétablir
add_variable: Ajouter une variable add_variable: Ajouter une variable
variable: Variable
enter_a_url_or_variable_name: Entrez une URL ou un nom de variable enter_a_url_or_variable_name: Entrez une URL ou un nom de variable
new_token: Nouveau jeton new_token: Nouveau jeton
token: Jeton token: Jeton
@ -4432,24 +4355,6 @@ pt: &pt
stripe_integration: Integração com Stripe stripe_integration: Integração com Stripe
stripe_account_has_been_connected: Conta Stripe foi conectada. stripe_account_has_been_connected: Conta Stripe foi conectada.
re_connect_stripe: Reconectar Stripe re_connect_stripe: Reconectar Stripe
paypal_integration: Integração com PayPal
paypal_account_has_been_connected: Conta PayPal foi conectada.
re_connect_paypal: Reconectar PayPal
failed_to_connect_paypal: Falha ao conectar PayPal. Tente novamente.
paypal_connect_not_completed: A conexão com PayPal não foi concluída.
payment_receipt: Recibo de pagamento
amount_paid: Valor pago
transaction: Transação
transaction_id: ID da transação
order_id: ID do pedido
payment_date: Data
paid_by: Pago por
paid_to: Pago a
business: Empresa
country: País
payer_id: ID do pagador
merchant_id: ID do comerciante
breakdown: Detalhamento
private: Privado private: Privado
_variables: Variáveis _variables: Variáveis
your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Seu endereço de e-mail foi liberado imediatamente. Se você estiver entrando em uma equipe, agora pode ser convidado para a equipe da sua empresa sem esperar o fim do período de exclusão. your_email_address_has_been_released_immediately_you_can_now_be_added_to_your_company_team_without_waiting_for_the_deletion_period_to_end: Seu endereço de e-mail foi liberado imediatamente. Se você estiver entrando em uma equipe, agora pode ser convidado para a equipe da sua empresa sem esperar o fim do período de exclusão.
@ -4861,7 +4766,7 @@ pt: &pt
back_to_active: Voltar para ativo back_to_active: Voltar para ativo
submissions: Submissões submissions: Submissões
templates: Modelos templates: Modelos
document_templates_html: Modelos <span class="hidden lg:inline">de documento</span> document_templates_html: Modelos <span class="hidden md:inline">de documento</span>
archived: Arquivado archived: Arquivado
search: Buscar search: Buscar
submissions_not_found: Submissões não encontradas submissions_not_found: Submissões não encontradas
@ -5350,7 +5255,6 @@ pt: &pt
undo: Desfazer undo: Desfazer
redo: Refazer redo: Refazer
add_variable: Adicionar variável add_variable: Adicionar variável
variable: Variável
enter_a_url_or_variable_name: Digite uma URL ou nome de variável enter_a_url_or_variable_name: Digite uma URL ou nome de variável
new_token: Novo token new_token: Novo token
token: Token token: Token
@ -5539,24 +5443,6 @@ de: &de
require_all_recipients: Alle Empfänger verpflichten require_all_recipients: Alle Empfänger verpflichten
stripe_account_has_been_connected: Stripe-Konto wurde verbunden. stripe_account_has_been_connected: Stripe-Konto wurde verbunden.
re_connect_stripe: Stripe erneut verbinden re_connect_stripe: Stripe erneut verbinden
paypal_integration: PayPal-Integration
paypal_account_has_been_connected: PayPal-Konto wurde verbunden.
re_connect_paypal: PayPal erneut verbinden
failed_to_connect_paypal: PayPal konnte nicht verbunden werden. Bitte erneut versuchen.
paypal_connect_not_completed: Die PayPal-Verbindung wurde nicht abgeschlossen.
payment_receipt: Zahlungsbeleg
amount_paid: Gezahlter Betrag
transaction: Transaktion
transaction_id: Transaktions-ID
order_id: Bestell-ID
payment_date: Datum
paid_by: Gezahlt von
paid_to: Gezahlt an
business: Unternehmen
country: Land
payer_id: Zahler-ID
merchant_id: Händler-ID
breakdown: Aufschlüsselung
bcc_recipients: BCC-Empfänger bcc_recipients: BCC-Empfänger
resend_pending: Ausstehende erneut versenden resend_pending: Ausstehende erneut versenden
always_enforce_signing_order: Reihenfolge der Unterschriften immer erzwingen always_enforce_signing_order: Reihenfolge der Unterschriften immer erzwingen
@ -5958,7 +5844,7 @@ de: &de
back_to_active: Zurück zu Aktiv back_to_active: Zurück zu Aktiv
submissions: Einreichungen submissions: Einreichungen
templates: Vorlagen templates: Vorlagen
document_templates_html: <span class="lg:hidden">Vorlagen</span><span class="hidden lg:inline">Dokumentvorlagen</span> document_templates_html: <span class="md:hidden">Vorlagen</span><span class="hidden md:inline">Dokumentvorlagen</span>
archived: Archiviert archived: Archiviert
search: Suchen search: Suchen
submissions_not_found: Einreichungen nicht gefunden submissions_not_found: Einreichungen nicht gefunden
@ -6447,7 +6333,6 @@ de: &de
undo: Rückgängig undo: Rückgängig
redo: Wiederholen redo: Wiederholen
add_variable: Variable hinzufügen add_variable: Variable hinzufügen
variable: Variable
enter_a_url_or_variable_name: Geben Sie eine URL oder einen Variablennamen ein enter_a_url_or_variable_name: Geben Sie eine URL oder einen Variablennamen ein
new_token: Neues Token new_token: Neues Token
token: Token token: Token
@ -7040,24 +6925,6 @@ nl: &nl
require_all_recipients: Alle ontvangers verplichten require_all_recipients: Alle ontvangers verplichten
stripe_account_has_been_connected: Stripe-account is verbonden. stripe_account_has_been_connected: Stripe-account is verbonden.
re_connect_stripe: Stripe opnieuw verbinden re_connect_stripe: Stripe opnieuw verbinden
paypal_integration: PayPal-integratie
paypal_account_has_been_connected: PayPal-account is verbonden.
re_connect_paypal: PayPal opnieuw verbinden
failed_to_connect_paypal: PayPal kon niet worden verbonden. Probeer het opnieuw.
paypal_connect_not_completed: De verbinding met PayPal is niet voltooid.
payment_receipt: Betalingsbewijs
amount_paid: Betaald bedrag
transaction: Transactie
transaction_id: Transactie-ID
order_id: Order-ID
payment_date: Datum
paid_by: Betaald door
paid_to: Betaald aan
business: Bedrijf
country: Land
payer_id: Betaler-ID
merchant_id: Verkoper-ID
breakdown: Specificatie
bcc_recipients: BCC-ontvangers bcc_recipients: BCC-ontvangers
resend_pending: Opnieuw verzenden in behandeling resend_pending: Opnieuw verzenden in behandeling
always_enforce_signing_order: Ondertekenvolgorde altijd afdwingen always_enforce_signing_order: Ondertekenvolgorde altijd afdwingen
@ -7460,7 +7327,7 @@ nl: &nl
back_to_active: Terug naar Actief back_to_active: Terug naar Actief
submissions: Inzendingen submissions: Inzendingen
templates: Sjablonen templates: Sjablonen
document_templates_html: <span class="lg:hidden">Sjablonen</span><span class="hidden lg:inline">Documentsjablonen</span> document_templates_html: <span class="md:hidden">Sjablonen</span><span class="hidden md:inline">Documentsjablonen</span>
archived: Gearchiveerd archived: Gearchiveerd
search: Zoeken search: Zoeken
submissions_not_found: Geen inzendingen gevonden submissions_not_found: Geen inzendingen gevonden
@ -7945,7 +7812,6 @@ nl: &nl
undo: Ongedaan maken undo: Ongedaan maken
redo: Opnieuw redo: Opnieuw
add_variable: Variabele toevoegen add_variable: Variabele toevoegen
variable: Variabele
enter_a_url_or_variable_name: Voer een URL of variabelenaam in enter_a_url_or_variable_name: Voer een URL of variabelenaam in
new_token: Nieuw token new_token: Nieuw token
token: Token token: Token

@ -178,8 +178,6 @@ Rails.application.routes.draw do
resources :send_email, only: %i[create], controller: 'submitters_send_email' resources :send_email, only: %i[create], controller: 'submitters_send_email'
end end
resources :settings, only: %i[index]
scope '/settings', as: :settings do scope '/settings', as: :settings do
unless Docuseal.multitenant? unless Docuseal.multitenant?
resources :storage, only: %i[index create], controller: 'storage_settings' resources :storage, only: %i[index create], controller: 'storage_settings'

@ -130,13 +130,13 @@ module Submissions
submission submission
end end
def maybe_set_dynamic_documents(submission, preview: false) def maybe_set_dynamic_documents(submission)
return submission unless submission.template_id? return submission unless submission.template_id?
template = submission.template template = submission.template
return submission if !preview && (template.variables_schema.present? || return submission if template.variables_schema.present? ||
submission.variables_schema.present?) submission.variables_schema.present?
return submission if template.schema.none? { |e| e['dynamic'] } return submission if template.schema.none? { |e| e['dynamic'] }

@ -49,7 +49,7 @@ module Submitters
end end
def fulltext_search_field(current_user, submitters, keyword, field_name) def fulltext_search_field(current_user, submitters, keyword, field_name)
keyword = keyword.delete("\0\\") keyword = keyword.delete("\0")
return submitters.none if keyword.blank? return submitters.none if keyword.blank?

@ -220,16 +220,9 @@ module Submitters
submitter.values submitter.values
end end
values = submission_values.merge(acc.compact_blank) formula = normalize_formula(formula, submitter.submission, submission_values:)
acc[field['uuid']] = acc[field['uuid']] = calculate_formula_value(formula, submission_values.merge(acc.compact_blank))
if field['type'] == 'text'
eval_text_formula_value(formula, values, submitter.submission)
else
normalized_formula = normalize_formula(formula, submitter.submission, submission_values:)
calculate_formula_value(normalized_formula, values)
end
end end
computed_values.compact_blank computed_values.compact_blank
@ -257,10 +250,6 @@ module Submitters
0 0
end end
def eval_text_formula_value(_formula, _values, _submission)
''
end
def replace_current_date_placeholders(submitter) def replace_current_date_placeholders(submitter)
submitter.values.each_with_object({}) do |(uuid, v), acc| submitter.values.each_with_object({}) do |(uuid, v), acc|
acc[uuid] = acc[uuid] =

Loading…
Cancel
Save