mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 14:42:32 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05d578d88f | ||
|
|
8773b30b10 | ||
|
|
1b636c5878 | ||
|
|
46e948ab99 | ||
|
|
889103aa5e | ||
|
|
558a14a5f2 | ||
|
|
72bbdb38bf | ||
|
|
3975a03cb4 | ||
|
|
e398327fb7 | ||
|
|
9266694d65 | ||
|
|
350c4b1c2a | ||
|
|
c3a933d87d | ||
|
|
970e53a5a3 | ||
|
|
520bac2ec7 | ||
|
|
e1eddd31c4 | ||
|
|
3c616a824c | ||
|
|
90d86b58c2 | ||
|
|
4332b4ebb8 | ||
|
|
e2f930f6f7 | ||
|
|
c303159c63 | ||
|
|
c1ce2bec00 | ||
|
|
3d2046f227 | ||
|
|
babaf1aa48 | ||
|
|
3f03d343ca | ||
|
|
110cdb5123 | ||
|
|
440d1114be | ||
|
|
9032517d4e | ||
|
|
7c71fefc47 | ||
|
|
e8bd8ff76f | ||
|
|
fed420cae1 | ||
|
|
910490528d | ||
|
|
ab810be250 | ||
|
|
178ee47093 | ||
|
|
6f90147a11 | ||
|
|
3ebaacbb7a | ||
|
|
64b39b53a2 | ||
|
|
2948910c25 | ||
|
|
1d20c79cf4 | ||
|
|
48f3b13f3e | ||
|
|
1fc1e9af16 | ||
|
|
ce70016603 | ||
|
|
e0e79e343d | ||
|
|
6568f81ee9 | ||
|
|
386f130d7a | ||
|
|
3ee41975fd | ||
|
|
d9f314898b | ||
|
|
4fc7412cac | ||
|
|
9adbfc52c2 | ||
|
|
1ad8d9196a | ||
|
|
e9172392b5 | ||
|
|
d6d94de328 | ||
|
|
77261f457c | ||
|
|
c258915031 | ||
|
|
c1db8bdd3b | ||
|
|
c700484ce8 | ||
|
|
b62bd200ed | ||
|
|
a164fe8dd8 | ||
|
|
bb40e872b3 | ||
|
|
bab80e9488 | ||
|
|
d090d79ebf | ||
|
|
2299ba4e29 | ||
|
|
d6474f0e6e | ||
|
|
e0a297cf81 | ||
|
|
40f5fa4217 | ||
|
|
94e552d170 | ||
|
|
cf1b0a4c16 | ||
|
|
9aa0a8ecbf | ||
|
|
fd4bef1706 | ||
|
|
d32b0e6d20 | ||
|
|
4a846be214 | ||
|
|
3dd3877ea9 | ||
|
|
b4dba0d809 | ||
|
|
dea80a82d8 | ||
|
|
eefa994724 | ||
|
|
35cce24539 | ||
|
|
e6c8203959 | ||
|
|
9479771fc8 | ||
|
|
711427521c | ||
|
|
5dae742361 | ||
|
|
d8ccd4beee | ||
|
|
c532a0115d | ||
|
|
c9c3364764 | ||
|
|
1ff23d7c40 | ||
|
|
95e9b34033 | ||
|
|
6b42dcf91b | ||
|
|
28962c949b | ||
|
|
4ecbbf8410 | ||
|
|
990b35be18 | ||
|
|
8002764855 | ||
|
|
b80982658c | ||
|
|
443ac23c29 | ||
|
|
37bb987e3e | ||
|
|
c7b08e8d30 | ||
|
|
dd502e8cef | ||
|
|
a554182e57 | ||
|
|
0c039ee64f | ||
|
|
c6b004f107 | ||
|
|
2fcb08d27d | ||
|
|
75fe5ce381 | ||
|
|
fd7d0eaf53 | ||
|
|
7e537e4232 | ||
|
|
a892975010 | ||
|
|
05ec92186b |
No files matched your search
+1
-1
@@ -66,7 +66,7 @@ RSpec/ExampleLength:
|
||||
Max: 40
|
||||
|
||||
RSpec/MultipleMemoizedHelpers:
|
||||
Max: 6
|
||||
Max: 9
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Max: 4
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ ENV OPENSSL_CONF=/app/openssl_legacy.cnf
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache sqlite-dev libpq-dev mariadb-dev vips-dev vips-poppler poppler-utils redis vips-heif gcompat ttf-freefont && mkdir /fonts && rm /usr/share/fonts/freefont/FreeSans.otf
|
||||
RUN echo '@edge https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories && apk add --no-cache sqlite-dev libpq-dev mariadb-dev vips-dev vips-poppler poppler-utils redis libheif@edge vips-heif gcompat ttf-freefont && mkdir /fonts && rm /usr/share/fonts/freefont/FreeSans.otf
|
||||
|
||||
RUN echo $'.include = /etc/ssl/openssl.cnf\n\
|
||||
\n\
|
||||
@@ -57,7 +57,7 @@ activate = 1' >> /app/openssl_legacy.cnf
|
||||
|
||||
COPY ./Gemfile ./Gemfile.lock ./
|
||||
|
||||
RUN apk add --no-cache build-base && bundle install && apk del build-base && rm -rf ~/.bundle /usr/local/bundle/cache && ruby -e "puts Dir['/usr/local/bundle/**/{spec,rdoc,resources/shared,resources/collation,resources/locales}']" | xargs rm -rf
|
||||
RUN apk add --no-cache build-base && bundle install && apk del --no-cache build-base && rm -rf ~/.bundle /usr/local/bundle/cache && ruby -e "puts Dir['/usr/local/bundle/**/{spec,rdoc,resources/shared,resources/collation,resources/locales}']" | xargs rm -rf
|
||||
|
||||
COPY ./bin ./bin
|
||||
COPY ./app ./app
|
||||
|
||||
@@ -23,7 +23,6 @@ gem 'image_processing'
|
||||
gem 'jwt'
|
||||
gem 'lograge'
|
||||
gem 'mysql2', require: false
|
||||
gem 'net-smtp', '0.4.0'
|
||||
gem 'oj'
|
||||
gem 'pagy'
|
||||
gem 'pg', require: false
|
||||
@@ -40,7 +39,7 @@ gem 'ruby-vips'
|
||||
gem 'rubyXL'
|
||||
gem 'shakapacker'
|
||||
gem 'sidekiq'
|
||||
gem 'sqlite3', require: false, force_ruby_platform: true
|
||||
gem 'sqlite3', require: false
|
||||
gem 'strip_attributes'
|
||||
gem 'turbo-rails'
|
||||
gem 'twitter_cldr', require: false
|
||||
|
||||
+176
-173
@@ -1,105 +1,101 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
actioncable (7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
activejob (= 7.1.3.4)
|
||||
activerecord (= 7.1.3.4)
|
||||
activestorage (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
actionview (= 7.1.3.4)
|
||||
activejob (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailbox (7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
activejob (= 7.2.1.1)
|
||||
activerecord (= 7.2.1.1)
|
||||
activestorage (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
mail (>= 2.8.0)
|
||||
actionmailer (7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
actionview (= 7.2.1.1)
|
||||
activejob (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
mail (>= 2.8.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.3.4)
|
||||
actionview (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
actionpack (7.2.1.1)
|
||||
actionview (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
rack (>= 2.2.4, < 3.2)
|
||||
rack-session (>= 1.0.1)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actiontext (7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
activerecord (= 7.1.3.4)
|
||||
activestorage (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
useragent (~> 0.16)
|
||||
actiontext (7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
activerecord (= 7.2.1.1)
|
||||
activestorage (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
actionview (7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
activejob (7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
activejob (7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
activerecord (7.1.3.4)
|
||||
activemodel (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
activemodel (7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
activerecord (7.2.1.1)
|
||||
activemodel (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
activejob (= 7.1.3.4)
|
||||
activerecord (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
activestorage (7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
activejob (= 7.2.1.1)
|
||||
activerecord (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
marcel (~> 1.0)
|
||||
activesupport (7.1.3.4)
|
||||
activesupport (7.2.1.1)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
mutex_m
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.6)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
annotate (3.2.0)
|
||||
activerecord (>= 3.2, < 8.0)
|
||||
rake (>= 10.4, < 14.0)
|
||||
arabic-letter-connector (0.1.1)
|
||||
ast (2.4.2)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.913.0)
|
||||
aws-sdk-core (3.191.6)
|
||||
aws-partitions (1.985.0)
|
||||
aws-sdk-core (3.209.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sigv4 (~> 1.9)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.79.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.146.1)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sdk-kms (1.94.0)
|
||||
aws-sdk-core (~> 3, >= 3.207.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.167.0)
|
||||
aws-sdk-core (~> 3, >= 3.207.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sdk-secretsmanager (1.91.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.8.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-secretsmanager (1.108.0)
|
||||
aws-sdk-core (~> 3, >= 3.207.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.10.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
azure-storage-blob (2.0.3)
|
||||
azure-storage-common (~> 2.0)
|
||||
@@ -120,14 +116,14 @@ GEM
|
||||
smart_properties
|
||||
bigdecimal (3.1.8)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.18.3)
|
||||
bootsnap (1.18.4)
|
||||
msgpack (~> 1.2)
|
||||
builder (3.3.0)
|
||||
bullet (7.1.6)
|
||||
bullet (7.2.0)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
camertron-eprun (1.1.1)
|
||||
cancancan (3.5.0)
|
||||
cancancan (3.6.1)
|
||||
capybara (3.40.0)
|
||||
addressable
|
||||
matrix
|
||||
@@ -137,7 +133,8 @@ GEM
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
xpath (~> 3.2)
|
||||
childprocess (5.0.0)
|
||||
childprocess (5.1.0)
|
||||
logger (~> 1.5)
|
||||
chunky_png (1.4.0)
|
||||
cldr-plurals-runtime-rb (1.1.0)
|
||||
cmdparse (3.0.7)
|
||||
@@ -148,12 +145,12 @@ GEM
|
||||
bigdecimal
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.17.1)
|
||||
css_parser (1.19.0)
|
||||
addressable
|
||||
csv (3.3.0)
|
||||
cuprite (0.15)
|
||||
cuprite (0.15.1)
|
||||
capybara (~> 3.0)
|
||||
ferrum (~> 0.14.0)
|
||||
ferrum (~> 0.15.0)
|
||||
date (3.3.4)
|
||||
debug (1.9.2)
|
||||
irb (~> 1.10)
|
||||
@@ -173,26 +170,26 @@ GEM
|
||||
diff-lcs (1.5.1)
|
||||
digest-crc (0.6.5)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
docile (1.4.0)
|
||||
dotenv (3.1.0)
|
||||
docile (1.4.1)
|
||||
dotenv (3.1.4)
|
||||
drb (2.2.1)
|
||||
email_typo (0.2.3)
|
||||
erb_lint (0.5.0)
|
||||
erb_lint (0.6.0)
|
||||
activesupport
|
||||
better_html (>= 2.0.1)
|
||||
parser (>= 2.7.1.4)
|
||||
rainbow
|
||||
rubocop
|
||||
rubocop (>= 1)
|
||||
smart_properties
|
||||
erubi (1.13.0)
|
||||
factory_bot (6.4.6)
|
||||
factory_bot (6.5.0)
|
||||
activesupport (>= 5.0.0)
|
||||
factory_bot_rails (6.4.3)
|
||||
factory_bot (~> 6.4)
|
||||
railties (>= 5.0.0)
|
||||
faker (3.3.1)
|
||||
faker (3.4.2)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (1.10.3)
|
||||
faraday (1.10.4)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
@@ -212,58 +209,60 @@ GEM
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http (1.0.2)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday_middleware (1.2.1)
|
||||
faraday (~> 1.0)
|
||||
ferrum (0.14)
|
||||
ferrum (0.15)
|
||||
addressable (~> 2.5)
|
||||
concurrent-ruby (~> 1.1)
|
||||
webrick (~> 1.7)
|
||||
websocket-driver (>= 0.6, < 0.8)
|
||||
ffi (1.16.3)
|
||||
websocket-driver (~> 0.7)
|
||||
ffi (1.17.0)
|
||||
ffi (1.17.0-aarch64-linux-gnu)
|
||||
ffi (1.17.0-x86_64-linux-gnu)
|
||||
geom2d (0.4.1)
|
||||
globalid (1.2.1)
|
||||
activesupport (>= 6.1)
|
||||
google-apis-core (0.14.1)
|
||||
google-apis-core (0.15.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 1.9)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
httpclient (>= 2.8.3, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
mutex_m
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
google-apis-iamcredentials_v1 (0.20.0)
|
||||
google-apis-core (>= 0.14.0, < 2.a)
|
||||
google-apis-storage_v1 (0.37.0)
|
||||
google-apis-core (>= 0.14.0, < 2.a)
|
||||
google-cloud-core (1.7.0)
|
||||
google-apis-iamcredentials_v1 (0.21.0)
|
||||
google-apis-core (>= 0.15.0, < 2.a)
|
||||
google-apis-storage_v1 (0.46.0)
|
||||
google-apis-core (>= 0.15.0, < 2.a)
|
||||
google-cloud-core (1.7.1)
|
||||
google-cloud-env (>= 1.0, < 3.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (2.1.1)
|
||||
google-cloud-env (2.2.1)
|
||||
faraday (>= 1.0, < 3.a)
|
||||
google-cloud-errors (1.4.0)
|
||||
google-cloud-storage (1.49.0)
|
||||
google-cloud-storage (1.52.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-core (~> 0.13)
|
||||
google-apis-iamcredentials_v1 (~> 0.18)
|
||||
google-apis-storage_v1 (~> 0.33)
|
||||
google-apis-storage_v1 (~> 0.38)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (~> 1.9)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.11.0)
|
||||
googleauth (1.11.1)
|
||||
faraday (>= 1.0, < 3.a)
|
||||
google-cloud-env (~> 2.1)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
hashdiff (1.1.0)
|
||||
hexapdf (0.42.0)
|
||||
hashdiff (1.1.1)
|
||||
hexapdf (0.47.0)
|
||||
cmdparse (~> 3.0, >= 3.0.3)
|
||||
geom2d (~> 0.4, >= 0.4.1)
|
||||
openssl (>= 2.2.1)
|
||||
@@ -271,28 +270,29 @@ GEM
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_processing (1.12.2)
|
||||
image_processing (1.13.0)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
io-console (0.7.2)
|
||||
irb (1.14.0)
|
||||
irb (1.14.1)
|
||||
rdoc (>= 4.0.0)
|
||||
reline (>= 0.4.2)
|
||||
jmespath (1.6.2)
|
||||
json (2.7.2)
|
||||
jwt (2.8.1)
|
||||
jwt (2.9.3)
|
||||
base64
|
||||
language_server-protocol (3.17.0.3)
|
||||
launchy (3.0.0)
|
||||
launchy (3.0.1)
|
||||
addressable (~> 2.8)
|
||||
childprocess (~> 5.0)
|
||||
letter_opener (1.10.0)
|
||||
launchy (>= 2.2, < 4)
|
||||
letter_opener_web (2.0.0)
|
||||
actionmailer (>= 5.2)
|
||||
letter_opener (~> 1.7)
|
||||
railties (>= 5.2)
|
||||
letter_opener_web (3.0.0)
|
||||
actionmailer (>= 6.1)
|
||||
letter_opener (~> 1.9)
|
||||
railties (>= 6.1)
|
||||
rexml
|
||||
logger (1.6.1)
|
||||
lograge (0.14.0)
|
||||
actionpack (>= 4)
|
||||
activesupport (>= 4)
|
||||
@@ -309,25 +309,25 @@ GEM
|
||||
marcel (1.0.4)
|
||||
matrix (0.4.2)
|
||||
method_source (1.1.0)
|
||||
mini_magick (4.12.0)
|
||||
mini_magick (4.13.2)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.7)
|
||||
minitest (5.25.1)
|
||||
msgpack (1.7.2)
|
||||
msgpack (1.7.3)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.4.0)
|
||||
multipart-post (2.4.1)
|
||||
mutex_m (0.2.0)
|
||||
mysql2 (0.5.6)
|
||||
net-http-persistent (4.0.2)
|
||||
net-http-persistent (4.0.4)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.4.14)
|
||||
net-imap (0.4.17)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
net-protocol
|
||||
net-protocol (0.2.2)
|
||||
timeout
|
||||
net-smtp (0.4.0)
|
||||
net-smtp (0.5.0)
|
||||
net-protocol
|
||||
nio4r (2.7.3)
|
||||
nokogiri (1.16.7)
|
||||
@@ -337,21 +337,23 @@ GEM
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.16.7-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
oj (3.16.3)
|
||||
oj (3.16.6)
|
||||
bigdecimal (>= 3.0)
|
||||
ostruct (>= 0.2)
|
||||
openssl (3.2.0)
|
||||
orm_adapter (0.5.0)
|
||||
os (1.1.4)
|
||||
ostruct (0.6.0)
|
||||
package_json (0.1.0)
|
||||
pagy (8.1.2)
|
||||
parallel (1.25.1)
|
||||
parser (3.3.4.0)
|
||||
pagy (9.1.0)
|
||||
parallel (1.26.3)
|
||||
parser (3.3.5.0)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
pg (1.5.6)
|
||||
premailer (1.23.0)
|
||||
pg (1.5.8)
|
||||
premailer (1.27.0)
|
||||
addressable
|
||||
css_parser (>= 1.12.0)
|
||||
css_parser (>= 1.19.0)
|
||||
htmlentities (>= 4.0.0)
|
||||
premailer-rails (1.12.0)
|
||||
actionmailer (>= 3)
|
||||
@@ -362,15 +364,15 @@ GEM
|
||||
pry (0.14.2)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
pry-rails (0.3.11)
|
||||
pry (>= 0.13.0)
|
||||
psych (5.1.2)
|
||||
stringio
|
||||
public_suffix (5.0.5)
|
||||
public_suffix (6.0.1)
|
||||
puma (6.4.3)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.8.1)
|
||||
rack (3.1.7)
|
||||
rack (3.1.8)
|
||||
rack-proxy (0.7.7)
|
||||
rack
|
||||
rack-session (2.0.0)
|
||||
@@ -380,20 +382,20 @@ GEM
|
||||
rackup (2.1.0)
|
||||
rack (>= 3)
|
||||
webrick (~> 1.8)
|
||||
rails (7.1.3.4)
|
||||
actioncable (= 7.1.3.4)
|
||||
actionmailbox (= 7.1.3.4)
|
||||
actionmailer (= 7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
actiontext (= 7.1.3.4)
|
||||
actionview (= 7.1.3.4)
|
||||
activejob (= 7.1.3.4)
|
||||
activemodel (= 7.1.3.4)
|
||||
activerecord (= 7.1.3.4)
|
||||
activestorage (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
rails (7.2.1.1)
|
||||
actioncable (= 7.2.1.1)
|
||||
actionmailbox (= 7.2.1.1)
|
||||
actionmailer (= 7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
actiontext (= 7.2.1.1)
|
||||
actionview (= 7.2.1.1)
|
||||
activejob (= 7.2.1.1)
|
||||
activemodel (= 7.2.1.1)
|
||||
activerecord (= 7.2.1.1)
|
||||
activestorage (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.1.3.4)
|
||||
railties (= 7.2.1.1)
|
||||
rails-dom-testing (2.2.0)
|
||||
activesupport (>= 5.0.0)
|
||||
minitest
|
||||
@@ -408,10 +410,10 @@ GEM
|
||||
actionview (> 3.1)
|
||||
activesupport (> 3.1)
|
||||
railties (> 3.1)
|
||||
railties (7.1.3.4)
|
||||
actionpack (= 7.1.3.4)
|
||||
activesupport (= 7.1.3.4)
|
||||
irb
|
||||
railties (7.2.1.1)
|
||||
actionpack (= 7.2.1.1)
|
||||
activesupport (= 7.2.1.1)
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0, >= 1.2.2)
|
||||
@@ -429,79 +431,80 @@ GEM
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
request_store (1.6.0)
|
||||
request_store (1.7.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
retriable (3.1.2)
|
||||
rexml (3.3.6)
|
||||
strscan
|
||||
rexml (3.3.8)
|
||||
rotp (6.3.0)
|
||||
rqrcode (2.2.0)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 1.0)
|
||||
rqrcode_core (1.2.0)
|
||||
rspec-core (3.13.0)
|
||||
rspec-core (3.13.1)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-expectations (3.13.0)
|
||||
rspec-expectations (3.13.3)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-mocks (3.13.0)
|
||||
rspec-mocks (3.13.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-rails (6.1.2)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
rspec-rails (7.0.1)
|
||||
actionpack (>= 7.0)
|
||||
activesupport (>= 7.0)
|
||||
railties (>= 7.0)
|
||||
rspec-core (~> 3.13)
|
||||
rspec-expectations (~> 3.13)
|
||||
rspec-mocks (~> 3.13)
|
||||
rspec-support (~> 3.13)
|
||||
rspec-support (3.13.1)
|
||||
rubocop (1.65.0)
|
||||
rubocop (1.66.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 2.4, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-ast (>= 1.32.2, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.31.3)
|
||||
rubocop-ast (1.32.3)
|
||||
parser (>= 3.3.1.0)
|
||||
rubocop-performance (1.21.0)
|
||||
rubocop-performance (1.22.1)
|
||||
rubocop (>= 1.48.1, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rails (2.24.1)
|
||||
rubocop-rails (2.26.2)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop (>= 1.52.0, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rspec (3.0.3)
|
||||
rubocop-rspec (3.1.0)
|
||||
rubocop (~> 1.61)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-vips (2.2.1)
|
||||
ruby-vips (2.2.2)
|
||||
ffi (~> 1.12)
|
||||
logger
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyXL (3.4.27)
|
||||
nokogiri (>= 1.10.8)
|
||||
rubyzip (>= 1.3.0)
|
||||
rubyzip (2.3.2)
|
||||
securerandom (0.3.1)
|
||||
semantic_range (3.0.0)
|
||||
shakapacker (8.0.0)
|
||||
shakapacker (8.0.2)
|
||||
activesupport (>= 5.2)
|
||||
package_json
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
sidekiq (7.2.4)
|
||||
sidekiq (7.3.2)
|
||||
concurrent-ruby (< 2)
|
||||
connection_pool (>= 2.3.0)
|
||||
logger
|
||||
rack (>= 2.2.4)
|
||||
redis-client (>= 0.19.0)
|
||||
redis-client (>= 0.22.2)
|
||||
signet (0.19.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
@@ -511,33 +514,34 @@ GEM
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov-html (0.13.1)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
smart_properties (1.17.0)
|
||||
sqlite3 (1.7.3)
|
||||
sqlite3 (2.1.0)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
sqlite3 (2.1.0-aarch64-linux-gnu)
|
||||
sqlite3 (2.1.0-x86_64-linux-gnu)
|
||||
stringio (3.1.1)
|
||||
strip_attributes (1.13.0)
|
||||
activemodel (>= 3.0, < 8.0)
|
||||
strscan (3.1.0)
|
||||
thor (1.3.2)
|
||||
timeout (0.4.1)
|
||||
trailblazer-option (0.1.2)
|
||||
turbo-rails (2.0.5)
|
||||
turbo-rails (2.0.10)
|
||||
actionpack (>= 6.0.0)
|
||||
activejob (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
twitter_cldr (6.12.0)
|
||||
twitter_cldr (6.12.1)
|
||||
camertron-eprun
|
||||
cldr-plurals-runtime-rb (~> 1.1)
|
||||
tzinfo
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2024.1)
|
||||
tzinfo-data (1.2024.2)
|
||||
tzinfo (>= 1.0.0)
|
||||
uber (0.1.0)
|
||||
unicode-display_width (2.5.0)
|
||||
unicode-display_width (2.6.0)
|
||||
uniform_notifier (1.16.0)
|
||||
useragent (0.16.10)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
web-console (4.2.1)
|
||||
@@ -545,17 +549,17 @@ GEM
|
||||
activemodel (>= 6.0.0)
|
||||
bindex (>= 0.4.0)
|
||||
railties (>= 6.0.0)
|
||||
webmock (3.23.0)
|
||||
webmock (3.24.0)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
webrick (1.8.1)
|
||||
webrick (1.8.2)
|
||||
websocket-driver (0.7.6)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.18)
|
||||
zeitwerk (2.7.0)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux
|
||||
@@ -592,7 +596,6 @@ DEPENDENCIES
|
||||
letter_opener_web
|
||||
lograge
|
||||
mysql2
|
||||
net-smtp (= 0.4.0)
|
||||
oj
|
||||
pagy
|
||||
pg
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
DocuSeal is an open source platform that provides secure and efficient digital document signing and processing. Create PDF forms to have them filled and signed online on any device with an easy-to-use, mobile-optimized web tool.
|
||||
</p>
|
||||
<h2 align="center">
|
||||
<a href="https://demo.docuseal.co">✨ Live Demo</a>
|
||||
<a href="https://demo.docuseal.tech">✨ Live Demo</a>
|
||||
<span>|</span>
|
||||
<a href="https://docuseal.co/sign_up">☁️ Try in Cloud</a>
|
||||
<a href="https://docuseal.com/sign_up">☁️ Try in Cloud</a>
|
||||
</h2>
|
||||
|
||||
[](https://demo.docuseal.co)
|
||||
[](https://demo.docuseal.tech)
|
||||
|
||||
## Features
|
||||
- PDF form fields builder (WYSIWYG)
|
||||
@@ -42,7 +42,7 @@ DocuSeal is an open source platform that provides secure and efficient digital d
|
||||
- PDF signature verification
|
||||
- Users management
|
||||
- Mobile-optimized
|
||||
- Signing available in 13 languages
|
||||
- 6 UI languages with signing available in 13 languages
|
||||
- API and Webhooks for integrations
|
||||
- Easy to deploy in minutes
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Reporting a Vulnerability
|
||||
|
||||
If you come across any security concern or vulnarability, please report the information via email to security@docuseal.co instead of opening a GitHub issue. We will promptly respond and will collaborate with you to validate the issue, and resolve it ASAP.
|
||||
If you come across any security concern or vulnarability, please report the information via email to security@docuseal.com instead of opening a GitHub issue. We will promptly respond and will collaborate with you to validate the issue, and resolve it ASAP.
|
||||
|
||||
**We have a bug bounty program to reward security researchers.**
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class AccountConfigsController < ApplicationController
|
||||
AccountConfig::ALLOW_TYPED_SIGNATURE,
|
||||
AccountConfig::FORCE_MFA,
|
||||
AccountConfig::ALLOW_TO_RESUBMIT,
|
||||
AccountConfig::ALLOW_TO_DECLINE_KEY,
|
||||
AccountConfig::FORM_PREFILL_SIGNATURE_KEY,
|
||||
AccountConfig::ESIGNING_PREFERENCE_KEY,
|
||||
AccountConfig::FORM_WITH_CONFETTI_KEY,
|
||||
|
||||
@@ -4,11 +4,11 @@ class AccountsController < ApplicationController
|
||||
LOCALE_OPTIONS = {
|
||||
'en-US' => 'English (United States)',
|
||||
'en-GB' => 'English (United Kingdom)',
|
||||
'fr-FR' => 'French (France)',
|
||||
'es-ES' => 'Spanish (Spain)',
|
||||
'pt-PT' => 'Portuguese (Portugal)',
|
||||
'de-DE' => 'German (Germany)',
|
||||
'it-IT' => 'Italian (Italy)'
|
||||
'fr-FR' => 'Français',
|
||||
'es-ES' => 'Español',
|
||||
'pt-PT' => 'Português',
|
||||
'de-DE' => 'Deutsch',
|
||||
'it-IT' => 'Italiano'
|
||||
}.freeze
|
||||
|
||||
before_action :load_account
|
||||
@@ -19,21 +19,25 @@ class AccountsController < ApplicationController
|
||||
def update
|
||||
current_account.update!(account_params)
|
||||
|
||||
@encrypted_config = EncryptedConfig.find_or_initialize_by(account: current_account,
|
||||
key: EncryptedConfig::APP_URL_KEY)
|
||||
@encrypted_config.assign_attributes(app_url_params)
|
||||
unless Docuseal.multitenant?
|
||||
@encrypted_config = EncryptedConfig.find_or_initialize_by(account: current_account,
|
||||
key: EncryptedConfig::APP_URL_KEY)
|
||||
@encrypted_config.assign_attributes(app_url_params)
|
||||
|
||||
unless URI.parse(@encrypted_config.value.to_s).class.in?([URI::HTTP, URI::HTTPS])
|
||||
@encrypted_config.errors.add(:value, I18n.t('should_be_a_valid_url'))
|
||||
unless URI.parse(@encrypted_config.value.to_s).class.in?([URI::HTTP, URI::HTTPS])
|
||||
@encrypted_config.errors.add(:value, I18n.t('should_be_a_valid_url'))
|
||||
|
||||
return render :show, status: :unprocessable_entity
|
||||
return render :show, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
@encrypted_config.save!
|
||||
|
||||
Docuseal.refresh_default_url_options!
|
||||
end
|
||||
|
||||
@encrypted_config.save!
|
||||
|
||||
Docuseal.refresh_default_url_options!
|
||||
|
||||
redirect_to settings_account_path, notice: I18n.t('account_information_has_been_updated')
|
||||
with_locale do
|
||||
redirect_to settings_account_path, notice: I18n.t('account_information_has_been_updated')
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render :show, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
@@ -38,12 +38,12 @@ module Api
|
||||
private
|
||||
|
||||
def authorization_check!(blob)
|
||||
is_authorized =
|
||||
blob.attachments.all? do |a|
|
||||
a.name.in?(%w[logo preview_images]) ||
|
||||
(current_user && a.record.account.id == current_user.account_id) ||
|
||||
!a.record.account.account_configs.find_or_initialize_by(key: AccountConfig::DOWNLOAD_LINKS_AUTH_KEY).value
|
||||
end
|
||||
attachment = blob.attachments.take
|
||||
|
||||
is_authorized = attachment.name.in?(%w[logo preview_images]) ||
|
||||
(current_user && attachment.record.account.id == current_user.account_id) ||
|
||||
!attachment.record.account.account_configs
|
||||
.find_or_initialize_by(key: AccountConfig::DOWNLOAD_LINKS_AUTH_KEY).value
|
||||
|
||||
return if is_authorized
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ module Api
|
||||
render json: { error: 'Too many requests' }, status: :too_many_requests
|
||||
end
|
||||
|
||||
if Rails.env.production?
|
||||
unless Rails.env.development?
|
||||
rescue_from CanCan::AccessDenied do |e|
|
||||
render json: { error: e.message }, status: :forbidden
|
||||
render json: { error: access_denied_error_message(e) }, status: :forbidden
|
||||
end
|
||||
|
||||
rescue_from JSON::ParserError do |e|
|
||||
@@ -39,6 +39,33 @@ module Api
|
||||
|
||||
private
|
||||
|
||||
def access_denied_error_message(error)
|
||||
return 'Not authorized' if request.headers['X-Auth-Token'].blank?
|
||||
return 'Not authorized' unless error.subject.is_a?(ActiveRecord::Base)
|
||||
return 'Not authorized' unless error.subject.respond_to?(:account_id)
|
||||
|
||||
linked_account_record_exists =
|
||||
if current_user.account.testing?
|
||||
current_user.account.linked_account_accounts.where(account_type: 'testing')
|
||||
.exists?(account_id: error.subject.account_id)
|
||||
else
|
||||
current_user.account.testing_accounts.exists?(id: error.subject.account_id)
|
||||
end
|
||||
|
||||
return 'Not authorized' unless linked_account_record_exists
|
||||
|
||||
object_name = error.subject.model_name.human
|
||||
id = error.subject.id
|
||||
|
||||
if current_user.account.testing?
|
||||
"#{object_name} #{id} not found using testing API key; Use production API key to " \
|
||||
"access production #{object_name.downcase.pluralize}."
|
||||
else
|
||||
"#{object_name} #{id} not found using production API key; Use testing API key to " \
|
||||
"access testing #{object_name.downcase.pluralize}."
|
||||
end
|
||||
end
|
||||
|
||||
def paginate(relation, field: :id)
|
||||
result = relation.order(field => :desc)
|
||||
.limit([params.fetch(:limit, DEFAULT_LIMIT).to_i, MAX_LIMIT].min)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class SubmissionDocumentsController < ApiBaseController
|
||||
load_and_authorize_resource :submission
|
||||
|
||||
def index
|
||||
documents =
|
||||
if @submission.submitters.all?(&:completed_at?)
|
||||
last_submitter = @submission.submitters.max_by(&:completed_at)
|
||||
|
||||
if last_submitter.documents_attachments.blank?
|
||||
last_submitter.documents_attachments = Submissions::EnsureResultGenerated.call(submitter)
|
||||
end
|
||||
|
||||
last_submitter.documents_attachments
|
||||
else
|
||||
values_hash = Submissions::GeneratePreviewAttachments.build_values_hash(@submission)
|
||||
|
||||
if @submission.preview_documents.present? &&
|
||||
@submission.preview_documents.all? { |s| s.metadata['values_hash'] == values_hash }
|
||||
@submission.preview_documents
|
||||
else
|
||||
ApplicationRecord.no_touching do
|
||||
@submission.preview_documents.each(&:destroy)
|
||||
end
|
||||
|
||||
Submissions::GeneratePreviewAttachments.call(@submission, values_hash:)
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: documents,
|
||||
associations: [:blob]
|
||||
).call
|
||||
|
||||
render json: {
|
||||
id: @submission.id,
|
||||
documents: documents.map do |attachment|
|
||||
{ name: attachment.filename.base, url: ActiveStorage::Blob.proxy_url(attachment.blob) }
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -74,13 +74,14 @@ module Api
|
||||
Submissions.send_signature_requests(submissions)
|
||||
|
||||
submissions.each do |submission|
|
||||
if submission.submitters.all?(&:completed_at?) && submission.submitters.last
|
||||
ProcessSubmitterCompletionJob.perform_async({ 'submitter_id' => submission.submitters.last.id })
|
||||
submission.submitters.each do |submitter|
|
||||
ProcessSubmitterCompletionJob.perform_async({ 'submitter_id' => submitter.id }) if submitter.completed_at?
|
||||
end
|
||||
end
|
||||
|
||||
render json: build_create_json(submissions)
|
||||
rescue Submitters::NormalizeValues::BaseError, DownloadUtils::UnableToDownload => e
|
||||
rescue Submitters::NormalizeValues::BaseError, Submissions::CreateFromSubmitters::BaseError,
|
||||
DownloadUtils::UnableToDownload => e
|
||||
Rollbar.warning(e) if defined?(Rollbar)
|
||||
|
||||
render json: { error: e.message }, status: :unprocessable_entity
|
||||
|
||||
@@ -120,6 +120,7 @@ module Api
|
||||
end&.dig('uuid')
|
||||
|
||||
submitter.email = Submissions.normalize_email(attrs[:email]) if attrs.key?(:email)
|
||||
submitter.name = attrs[:name] if attrs.key?(:name)
|
||||
|
||||
if attrs.key?(:phone)
|
||||
submitter.phone = attrs[:phone].to_s.gsub(/[^0-9+]/, '')
|
||||
|
||||
@@ -20,10 +20,7 @@ module Api
|
||||
pdf = HexaPDF::Document.new(io: StringIO.new(file))
|
||||
|
||||
trusted_certs = Accounts.load_trusted_certs(current_account)
|
||||
|
||||
is_checksum_found = ActiveStorage::Attachment.joins(:blob)
|
||||
.where(name: 'documents', record_type: 'Submitter')
|
||||
.exists?(blob: { checksum: Digest::MD5.base64digest(file) })
|
||||
is_checksum_found = CompletedDocument.exists?(sha256: Base64.urlsafe_encode64(Digest::SHA256.digest(file)))
|
||||
|
||||
render json: {
|
||||
checksum_status: is_checksum_found ? 'verified' : 'not_found',
|
||||
|
||||
@@ -8,6 +8,7 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
check_authorization unless: :devise_controller?
|
||||
|
||||
around_action :with_locale
|
||||
before_action :sign_in_for_demo, if: -> { Docuseal.demo? }
|
||||
before_action :maybe_redirect_to_setup, unless: :signed_in?
|
||||
before_action :authenticate_user!, unless: :devise_controller?
|
||||
@@ -37,6 +38,10 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def default_url_options
|
||||
if request.domain == 'docuseal.com'
|
||||
return { host: 'docuseal.com', protocol: ENV['FORCE_SSL'].present? ? 'https' : 'http' }
|
||||
end
|
||||
|
||||
Docuseal.default_url_options
|
||||
end
|
||||
|
||||
@@ -51,7 +56,18 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
private
|
||||
|
||||
def with_locale(&)
|
||||
return yield unless current_account
|
||||
|
||||
locale = params[:lang].presence if Rails.env.development?
|
||||
locale ||= current_account.locale
|
||||
|
||||
I18n.with_locale(locale, &)
|
||||
end
|
||||
|
||||
def with_browser_locale(&)
|
||||
return yield if I18n.locale != :'en-US' && I18n.locale != :en
|
||||
|
||||
locale = params[:lang].presence
|
||||
locale ||= request.env['HTTP_ACCEPT_LANGUAGE'].to_s[BROWSER_LOCALE_REGEXP].to_s
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class EmailSmtpSettingsController < ApplicationController
|
||||
|
||||
def create
|
||||
if @encrypted_config.update(email_configs)
|
||||
SettingsMailer.smtp_successful_setup(@encrypted_config.value['from_email']).deliver_now!
|
||||
SettingsMailer.smtp_successful_setup(@encrypted_config.value['from_email'] || current_user.email).deliver_now!
|
||||
|
||||
redirect_to settings_email_index_path, notice: I18n.t('changes_have_been_saved')
|
||||
else
|
||||
|
||||
@@ -21,9 +21,9 @@ class EsignSettingsController < ApplicationController
|
||||
default_pkcs = GenerateCertificate.load_pkcs(cert_data) if cert_data['cert'].present?
|
||||
|
||||
custom_pkcs_list = (cert_data['custom'] || []).map do |e|
|
||||
{ 'pkcs' => OpenSSL::PKCS12.new(Base64.urlsafe_decode64(e['data']), e['password'].to_s),
|
||||
'name' => e['name'],
|
||||
'status' => e['status'] }
|
||||
pkcs = e['data'].present? ? OpenSSL::PKCS12.new(Base64.urlsafe_decode64(e['data']), e['password'].to_s) : nil
|
||||
|
||||
{ 'pkcs' => pkcs, 'name' => e['name'], 'status' => e['status'] }
|
||||
end
|
||||
|
||||
@pkcs_list = [
|
||||
@@ -56,7 +56,7 @@ class EsignSettingsController < ApplicationController
|
||||
|
||||
save_new_cert!(@encrypted_config, @cert_record)
|
||||
|
||||
redirect_to settings_esign_path, notice: I18n.t('certificate_has_been_successfully_added_')
|
||||
redirect_to settings_esign_path, notice: I18n.t('certificate_has_been_successfully_added')
|
||||
rescue OpenSSL::PKCS12::PKCS12Error => e
|
||||
Rollbar.error(e) if defined?(Rollbar)
|
||||
|
||||
@@ -66,10 +66,16 @@ class EsignSettingsController < ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
@encrypted_config.value['custom'].each { |e| e['status'] = 'validate' }
|
||||
@encrypted_config.value['custom'].to_a.each { |e| e['status'] = 'validate' }
|
||||
|
||||
custom_cert_data = @encrypted_config.value['custom'].find { |e| e['name'] == params[:name] }
|
||||
custom_cert_data['status'] = 'default' if custom_cert_data
|
||||
custom_cert_data = @encrypted_config.value['custom'].to_a.find { |e| e['name'] == params[:name] }
|
||||
|
||||
if custom_cert_data
|
||||
custom_cert_data['status'] = 'default'
|
||||
elsif params[:name] == Docuseal::AATL_CERT_NAME
|
||||
@encrypted_config.value['custom'] ||= []
|
||||
@encrypted_config.value['custom'] << { 'name' => params[:name], 'status' => 'default' }
|
||||
end
|
||||
|
||||
@encrypted_config.save!
|
||||
|
||||
@@ -81,7 +87,7 @@ class EsignSettingsController < ApplicationController
|
||||
|
||||
@encrypted_config.save!
|
||||
|
||||
redirect_to settings_esign_path, notice: I18m.t('certificate_has_been_removed')
|
||||
redirect_to settings_esign_path, notice: I18n.t('certificate_has_been_removed')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -12,7 +12,8 @@ class PreviewDocumentPageController < ActionController::API
|
||||
|
||||
return head :not_found unless attachment
|
||||
|
||||
preview_image = attachment.preview_images.joins(:blob).find_by(blob: { filename: "#{params[:id]}#{FORMAT}" })
|
||||
preview_image = attachment.preview_images.joins(:blob)
|
||||
.find_by(blob: { filename: ["#{params[:id]}.png", "#{params[:id]}.jpg"] })
|
||||
|
||||
return redirect_to preview_image.url, allow_other_host: true if preview_image
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class SetupController < ApplicationController
|
||||
def account_params
|
||||
return {} unless params[:account]
|
||||
|
||||
params.require(:account).permit(:name, :timezone)
|
||||
params.require(:account).permit(:name, :timezone, :locale)
|
||||
end
|
||||
|
||||
def encrypted_config_params
|
||||
|
||||
@@ -9,6 +9,13 @@ class SubmissionsController < ApplicationController
|
||||
def show
|
||||
@submission = Submissions.preload_with_pages(@submission)
|
||||
|
||||
unless @submission.submitters.all?(&:completed_at?)
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: [@submission],
|
||||
associations: [submitters: :start_form_submission_events]
|
||||
).call
|
||||
end
|
||||
|
||||
render :show, layout: 'plain'
|
||||
end
|
||||
|
||||
|
||||
@@ -15,6 +15,6 @@ class SubmissionsDashboardController < ApplicationController
|
||||
@submissions = @submissions.pending if params[:status] == 'pending'
|
||||
@submissions = @submissions.completed if params[:status] == 'completed'
|
||||
|
||||
@pagy, @submissions = pagy(@submissions.preload(:submitters).order(id: :desc))
|
||||
@pagy, @submissions = pagy(@submissions.preload(submitters: :start_form_submission_events).order(id: :desc))
|
||||
end
|
||||
end
|
||||
@@ -12,16 +12,20 @@ class SubmitFormController < ApplicationController
|
||||
def show
|
||||
@submitter = Submitter.find_by!(slug: params[:slug])
|
||||
|
||||
submission = @submitter.submission
|
||||
|
||||
return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at?
|
||||
return render :archived if @submitter.submission.template.archived_at? || @submitter.submission.archived_at?
|
||||
return render :expired if @submitter.submission.expired?
|
||||
return render :archived if submission.template.archived_at? || submission.archived_at?
|
||||
return render :expired if submission.expired?
|
||||
return render :declined if @submitter.declined_at?
|
||||
return render :awaiting if submission.template.preferences['submitters_order'] == 'preserved' &&
|
||||
!Submitters.current_submitter_order?(@submitter)
|
||||
|
||||
Submitters.preload_with_pages(@submitter)
|
||||
|
||||
Submitters::MaybeUpdateDefaultValues.call(@submitter, current_user)
|
||||
|
||||
@attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments)
|
||||
@attachments_index = ActiveStorage::Attachment.where(record: submission.submitters, name: :attachments)
|
||||
.preload(:blob).index_by(&:uuid)
|
||||
|
||||
@form_configs = Submitters::FormConfigs.call(@submitter, CONFIG_KEYS)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmittersController < ApplicationController
|
||||
load_and_authorize_resource :submitter, only: %i[edit update]
|
||||
|
||||
def edit
|
||||
@submitter_email_message =
|
||||
if @submitter.preferences['email_message_uuid'].present?
|
||||
@submitter.account.email_messages.find_by(uuid: @submitter.preferences['email_message_uuid'])
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
submission = @submitter.submission
|
||||
|
||||
if @submitter.submission_events.exists?(event_type: 'start_form') || submission.archived_at? || submission.expired?
|
||||
return redirect_back fallback_location: submission_path(submission), alert: I18n.t('submitter_cannot_be_updated')
|
||||
end
|
||||
|
||||
if submitter_params.values.all?(&:blank?)
|
||||
return redirect_back fallback_location: submission_path(submission),
|
||||
alert: I18n.t('at_least_one_field_must_be_filled')
|
||||
end
|
||||
|
||||
if params[:is_custom_message] != '1'
|
||||
params.delete(:subject)
|
||||
params.delete(:body)
|
||||
end
|
||||
|
||||
submitter_preferences = Submitters.normalize_preferences(@submitter.account, current_user, params)
|
||||
|
||||
if submitter_preferences.key?('email_message_uuid')
|
||||
@submitter.preferences['email_message_uuid'] = submitter_preferences['email_message_uuid']
|
||||
end
|
||||
|
||||
assign_submitter_attrs(@submitter, submitter_params)
|
||||
|
||||
if @submitter.save
|
||||
maybe_resend_email_sms(@submitter, params)
|
||||
|
||||
redirect_back fallback_location: submission_path(submission), notice: I18n.t('changes_have_been_saved')
|
||||
else
|
||||
redirect_back fallback_location: submission_path(submission), alert: I18n.t('unable_to_save')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def maybe_resend_email_sms(submitter, params)
|
||||
if params[:send_email] == '1' && submitter.email.present?
|
||||
is_sent_recently = Docuseal.multitenant? &&
|
||||
EmailEvent.exists?(email: submitter.email,
|
||||
tag: 'submitter_invitation',
|
||||
emailable: submitter,
|
||||
event_type: 'send',
|
||||
created_at: 4.hours.ago..Time.current)
|
||||
|
||||
SendSubmitterInvitationEmailJob.perform_async('submitter_id' => submitter.id) unless is_sent_recently
|
||||
end
|
||||
|
||||
return if submitter.phone.blank?
|
||||
return unless params[:send_sms] == '1'
|
||||
|
||||
SendSubmitterInvitationSmsJob.perform_async('submitter_id' => submitter.id)
|
||||
end
|
||||
|
||||
def assign_submitter_attrs(submitter, attrs)
|
||||
submitter.phone = attrs[:phone].to_s.gsub(/[^0-9+]/, '') if attrs.key?(:phone)
|
||||
|
||||
submitter.email = Submissions.normalize_email(attrs[:email]) if attrs.key?(:email)
|
||||
|
||||
submitter.name = attrs[:name] if attrs.key?(:name)
|
||||
|
||||
submitter
|
||||
end
|
||||
|
||||
def submitter_params
|
||||
params.require(:submitter).permit(:email, :name, :phone).transform_values(&:strip)
|
||||
end
|
||||
end
|
||||
@@ -7,7 +7,7 @@ class TemplateFoldersController < ApplicationController
|
||||
@templates = @template_folder.templates.active.preload(:author).order(id: :desc)
|
||||
@templates = Templates.search(@templates, params[:q])
|
||||
|
||||
@pagy, @templates = pagy(@templates, items: 12)
|
||||
@pagy, @templates = pagy(@templates, limit: 12)
|
||||
end
|
||||
|
||||
def edit; end
|
||||
|
||||
@@ -7,6 +7,6 @@ class TemplatesArchivedController < ApplicationController
|
||||
@templates = @templates.where.not(archived_at: nil).preload(:author, :folder).order(id: :desc)
|
||||
@templates = Templates.search(@templates, params[:q])
|
||||
|
||||
@pagy, @templates = pagy(@templates, items: 12)
|
||||
@pagy, @templates = pagy(@templates, limit: 12)
|
||||
end
|
||||
end
|
||||
@@ -15,7 +15,7 @@ class TemplatesController < ApplicationController
|
||||
submissions = submissions.pending if params[:status] == 'pending'
|
||||
submissions = submissions.completed if params[:status] == 'completed'
|
||||
|
||||
@pagy, @submissions = pagy(submissions.preload(:submitters).order(id: :desc))
|
||||
@pagy, @submissions = pagy(submissions.preload(submitters: :start_form_submission_events).order(id: :desc))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
redirect_to root_path
|
||||
end
|
||||
@@ -116,7 +116,8 @@ class TemplatesController < ApplicationController
|
||||
end
|
||||
|
||||
def authorized_clone_account_id?(account_id)
|
||||
true_user.account_id.to_s == account_id.to_s || true_user.account.linked_accounts.exists?(id: account_id)
|
||||
true_user.account_id.to_s == account_id.to_s ||
|
||||
true_user.account.linked_accounts.accessible_by(current_ability).exists?(id: account_id)
|
||||
end
|
||||
|
||||
def maybe_redirect_to_template(template)
|
||||
|
||||
@@ -23,14 +23,14 @@ class TemplatesDashboardController < ApplicationController
|
||||
@template_folders = @template_folders.reject { |e| e.name == TemplateFolder::DEFAULT_NAME }
|
||||
@templates = filter_templates(@templates)
|
||||
|
||||
items =
|
||||
limit =
|
||||
if @template_folders.size < 4
|
||||
TEMPLATES_PER_PAGE
|
||||
else
|
||||
(@template_folders.size < 7 ? 9 : 6)
|
||||
end
|
||||
|
||||
@pagy, @templates = pagy(@templates, items:)
|
||||
@pagy, @templates = pagy(@templates, limit:)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class TemplatesPreferencesController < ApplicationController
|
||||
documents_copy_email_enabled documents_copy_email_attach_audit
|
||||
completed_notification_email_attach_documents
|
||||
completed_redirect_url
|
||||
submitters_order
|
||||
completed_notification_email_subject completed_notification_email_body
|
||||
completed_notification_email_enabled completed_notification_email_attach_audit] +
|
||||
[completed_message: %i[title body]]
|
||||
|
||||
@@ -9,7 +9,7 @@ class UserSignaturesController < ApplicationController
|
||||
def update
|
||||
file = params[:file]
|
||||
|
||||
return redirect_to settings_profile_index_path, notice: I18n.t('Unable to save signature') if file.blank?
|
||||
return redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_signature') if file.blank?
|
||||
|
||||
blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
|
||||
filename: file.original_filename,
|
||||
@@ -24,7 +24,7 @@ class UserSignaturesController < ApplicationController
|
||||
if @user_config.update(value: attachment.uuid)
|
||||
redirect_to settings_profile_index_path, notice: I18n.t('signature_has_been_saved')
|
||||
else
|
||||
redirect_to settings_profile_index_path, notice: I18n.t('Unable to save signature')
|
||||
redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_signature')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ class UsersController < ApplicationController
|
||||
def index
|
||||
@users =
|
||||
if params[:status] == 'archived'
|
||||
@users.archived
|
||||
@users.archived.where.not(role: 'integration')
|
||||
elsif params[:status] == 'integration'
|
||||
@users.active.where(role: 'integration')
|
||||
else
|
||||
@users.active
|
||||
@users.active.where.not(role: 'integration')
|
||||
end
|
||||
|
||||
@pagy, @users = pagy(@users.where(account: current_account).order(id: :desc))
|
||||
|
||||
@@ -29,6 +29,7 @@ import SearchInput from './elements/search_input'
|
||||
import ToggleAttribute from './elements/toggle_attribute'
|
||||
import LinkedInput from './elements/linked_input'
|
||||
import CheckboxGroup from './elements/checkbox_group'
|
||||
import MaskedInput from './elements/masked_input'
|
||||
|
||||
import * as TurboInstantClick from './lib/turbo_instant_click'
|
||||
|
||||
@@ -95,6 +96,7 @@ safeRegisterElement('search-input', SearchInput)
|
||||
safeRegisterElement('toggle-attribute', ToggleAttribute)
|
||||
safeRegisterElement('linked-input', LinkedInput)
|
||||
safeRegisterElement('checkbox-group', CheckboxGroup)
|
||||
safeRegisterElement('masked-input', MaskedInput)
|
||||
|
||||
safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
@@ -150,7 +152,8 @@ safeRegisterElement('import-list', class extends HTMLElement {
|
||||
this.app = createApp(ImportList, {
|
||||
template: JSON.parse(this.dataset.template),
|
||||
multitenant: this.dataset.multitenant === 'true',
|
||||
authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content
|
||||
authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content,
|
||||
i18n: JSON.parse(this.dataset.i18n || '{}')
|
||||
})
|
||||
|
||||
this.app.mount(this.appElem)
|
||||
|
||||
@@ -51,10 +51,6 @@ button[disabled] .enabled {
|
||||
@apply input input-bordered bg-white;
|
||||
}
|
||||
|
||||
.base-input-slim {
|
||||
@apply input input-bordered bg-white h-10;
|
||||
}
|
||||
|
||||
.base-textarea {
|
||||
@apply textarea textarea-bordered bg-white rounded-3xl;
|
||||
}
|
||||
@@ -134,7 +130,3 @@ button[disabled] .enabled {
|
||||
outline-offset: 3px;
|
||||
outline-color: hsl(var(--bc) / 0.2);
|
||||
}
|
||||
|
||||
select:required:invalid {
|
||||
color: gray !important;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export default class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
const maskedToken = this.input.value
|
||||
|
||||
this.input.addEventListener('focus', () => {
|
||||
this.input.value = this.dataset.token
|
||||
this.input.select()
|
||||
})
|
||||
|
||||
this.input.addEventListener('focusout', () => {
|
||||
this.input.value = maskedToken
|
||||
})
|
||||
}
|
||||
|
||||
get input () {
|
||||
return this.querySelector('input')
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,14 @@ safeRegisterElement('submission-form', class extends HTMLElement {
|
||||
isDemo: this.dataset.isDemo === 'true',
|
||||
attribution: this.dataset.attribution !== 'false',
|
||||
scrollPadding: this.dataset.scrollPadding || '-80px',
|
||||
language: this.dataset.language,
|
||||
dryRun: this.dataset.dryRun === 'true',
|
||||
expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null,
|
||||
withSignatureId: this.dataset.withSignatureId === 'true',
|
||||
requireSigningReason: this.dataset.requireSigningReason === 'true',
|
||||
withConfetti: this.dataset.withConfetti !== 'false',
|
||||
withDisclosure: this.dataset.withDisclosure === 'true',
|
||||
reuseSignature: this.dataset.reuseSignature !== 'false',
|
||||
withTypedSignature: this.dataset.withTypedSignature !== 'false',
|
||||
authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content,
|
||||
rememberSignature: this.dataset.rememberSignature === 'true',
|
||||
|
||||
@@ -31,10 +31,6 @@ button[disabled] .enabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
select:required:invalid {
|
||||
@apply text-gray-300;
|
||||
}
|
||||
|
||||
.input-bordered {
|
||||
@apply border-base-content/20;
|
||||
}
|
||||
|
||||
@@ -405,7 +405,8 @@ export default {
|
||||
}
|
||||
|
||||
if (this.field.preferences?.font_size) {
|
||||
style.fontSize = this.field.preferences.font_size + 'pt'
|
||||
style.fontSize = `clamp(4pt, 1.6vw, ${this.field.preferences.font_size}pt)`
|
||||
style.lineHeight = `clamp(6pt, 2.0vw, ${parseInt(this.field.preferences.font_size) + 3}pt)`
|
||||
}
|
||||
|
||||
return style
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
id="form_completed"
|
||||
class="mx-auto max-w-md flex flex-col"
|
||||
dir="auto"
|
||||
>
|
||||
@@ -75,7 +76,7 @@
|
||||
</a>
|
||||
<a
|
||||
v-if="isDemo"
|
||||
href="https://docuseal.co/sign_up"
|
||||
href="https://docuseal.com/sign_up"
|
||||
class="white-button flex items-center space-x-1 w-full"
|
||||
>
|
||||
<IconLogin />
|
||||
@@ -222,7 +223,7 @@ export default {
|
||||
this.downloadUrls(urls)
|
||||
}
|
||||
} else {
|
||||
alert('Failed to download files')
|
||||
alert(this.t('failed_to_download_files'))
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
:name="`values[${field.uuid}]`"
|
||||
@keydown.enter="onEnter"
|
||||
@focus="$emit('focus')"
|
||||
@paste="onPaste"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,6 +99,25 @@ export default {
|
||||
this.$emit('submit')
|
||||
}
|
||||
},
|
||||
onPaste (e) {
|
||||
e.preventDefault()
|
||||
|
||||
let pasteData = e.clipboardData.getData('text').trim()
|
||||
|
||||
if (pasteData.match(/^\d{2}\.\d{2}\.\d{4}$/)) {
|
||||
pasteData = pasteData.split('.').reverse().join('-')
|
||||
}
|
||||
|
||||
const parsedDate = new Date(pasteData)
|
||||
|
||||
if (!isNaN(parsedDate)) {
|
||||
const inputEl = this.$refs.input
|
||||
|
||||
inputEl.valueAsDate = new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000)
|
||||
|
||||
inputEl.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
}
|
||||
},
|
||||
setCurrentDate () {
|
||||
const inputEl = this.$refs.input
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
v-model="values[currentField.uuid]"
|
||||
:show-field-names="showFieldNames"
|
||||
:field="currentField"
|
||||
@submit="submitStep"
|
||||
@submit="!isSubmitting && submitStep()"
|
||||
@focus="scrollIntoField(currentField)"
|
||||
/>
|
||||
<div v-else-if="currentField.type === 'select'">
|
||||
@@ -152,6 +152,7 @@
|
||||
dir="auto"
|
||||
:required="currentField.required"
|
||||
class="select base-input !text-2xl w-full text-center font-normal"
|
||||
:class="{ 'text-gray-300': !values[currentField.uuid] }"
|
||||
:name="`values[${currentField.uuid}]`"
|
||||
@change="values[currentField.uuid] = $event.target.value"
|
||||
@focus="scrollIntoField(currentField)"
|
||||
@@ -159,6 +160,7 @@
|
||||
<option
|
||||
value=""
|
||||
:selected="!values[currentField.uuid]"
|
||||
class="text-gray-300"
|
||||
>
|
||||
{{ t('select_your_option') }}
|
||||
</option>
|
||||
@@ -167,6 +169,7 @@
|
||||
:key="option.uuid"
|
||||
:selected="values[currentField.uuid] == option.value"
|
||||
:value="option.value"
|
||||
class="text-base-content"
|
||||
>
|
||||
{{ option.value }}
|
||||
</option>
|
||||
@@ -392,7 +395,7 @@
|
||||
:default-value="submitter.phone"
|
||||
:submitter-slug="submitterSlug"
|
||||
@focus="scrollIntoField(currentField)"
|
||||
@submit="submitStep"
|
||||
@submit="!isSubmitting && submitStep()"
|
||||
/>
|
||||
<PaymentStep
|
||||
v-else-if="currentField.type === 'payment'"
|
||||
@@ -404,7 +407,7 @@
|
||||
:values="values"
|
||||
@attached="attachments.push($event)"
|
||||
@focus="scrollIntoField(currentField)"
|
||||
@submit="submitStep"
|
||||
@submit="!isSubmitting && submitStep()"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@@ -897,7 +900,7 @@ export default {
|
||||
mounted () {
|
||||
this.submittedValues = JSON.parse(JSON.stringify(this.values))
|
||||
|
||||
screen?.orientation.addEventListener('change', this.onOrientationChange)
|
||||
screen?.orientation?.addEventListener('change', this.onOrientationChange)
|
||||
|
||||
this.fields.forEach((field) => {
|
||||
if (field.default_value && !field.readonly) {
|
||||
@@ -1104,6 +1107,10 @@ export default {
|
||||
const currentFieldUuids = this.currentStepFields.map((f) => f.uuid)
|
||||
const currentFieldType = this.currentField.type
|
||||
|
||||
if (!formData && !this.$refs.form.checkValidity()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.dryRun) {
|
||||
currentFieldUuids.forEach((fieldUuid) => {
|
||||
this.submittedValues[fieldUuid] = this.values[fieldUuid]
|
||||
@@ -1142,13 +1149,15 @@ export default {
|
||||
async submitStep () {
|
||||
this.isSubmitting = true
|
||||
|
||||
const submitStep = this.currentStep
|
||||
|
||||
const stepPromise = ['signature', 'phone', 'initials', 'payment'].includes(this.currentField.type)
|
||||
? this.$refs.currentStep.submit
|
||||
: () => Promise.resolve({})
|
||||
|
||||
stepPromise().then(async () => {
|
||||
const emptyRequiredField = this.stepFields.find((fields, index) => {
|
||||
if (index >= this.currentStep) {
|
||||
if (index >= submitStep) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1158,7 +1167,7 @@ export default {
|
||||
})
|
||||
|
||||
const formData = new FormData(this.$refs.form)
|
||||
const isLastStep = this.currentStep === this.stepFields.length - 1
|
||||
const isLastStep = submitStep === this.stepFields.length - 1
|
||||
|
||||
if (isLastStep && !emptyRequiredField && !this.inviteSubmitters.length) {
|
||||
formData.append('completed', 'true')
|
||||
@@ -1183,11 +1192,7 @@ export default {
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
|
||||
if (isLastStep) {
|
||||
this.isSecondWalkthrough = true
|
||||
}
|
||||
|
||||
const nextStep = (isLastStep && emptyRequiredField) || this.stepFields[this.currentStep + 1]
|
||||
const nextStep = (isLastStep && emptyRequiredField) || this.stepFields[submitStep + 1]
|
||||
|
||||
if (nextStep) {
|
||||
if (this.alwaysMinimize) {
|
||||
@@ -1211,7 +1216,7 @@ export default {
|
||||
})
|
||||
}).catch(error => {
|
||||
if (error?.message === 'Image too small') {
|
||||
alert('Signature is too small - please redraw.')
|
||||
alert(this.t('signature_is_too_small_please_redraw'))
|
||||
} else {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ const it = {
|
||||
pay: 'Pagamento',
|
||||
file: 'File',
|
||||
select: 'Seleziona',
|
||||
checkbox: 'Casella di controllo',
|
||||
checkbox: 'Checkbox',
|
||||
multiple: 'Multiplo',
|
||||
radio: 'Radio',
|
||||
cells: 'Celle',
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -61,30 +61,55 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
<div
|
||||
v-show="!isCodeSent"
|
||||
:id="field.uuid"
|
||||
ref="phone"
|
||||
:value="modelValue || defaultValue"
|
||||
:readonly="!!defaultValue"
|
||||
class="base-input !text-2xl w-full"
|
||||
autocomplete="tel"
|
||||
pattern="^\+[0-9\s\-]+$"
|
||||
type="tel"
|
||||
inputmode="tel"
|
||||
:required="field.required"
|
||||
placeholder="+1 234 567-8900"
|
||||
:name="`values[${field.uuid}]`"
|
||||
@invalid="$event.target.value ? $event.target.setCustomValidity(`${t('use_international_format')}...`) : ''"
|
||||
@input="[$event.target.setCustomValidity(''), $emit('update:model-value', $event.target.value)]"
|
||||
@focus="$emit('focus')"
|
||||
class="flex w-full rounded-full outline-neutral-content outline-2 outline-offset-2 focus-within:outline"
|
||||
>
|
||||
<div class="relative inline-block">
|
||||
<div class="btn bg-base-200 border border-neutral-300 text-2xl whitespace-nowrap font-normal rounded-l-full">
|
||||
{{ selectedCountry.flag }} +{{ selectedCountry.dial }}
|
||||
</div>
|
||||
<select
|
||||
class="absolute top-0 bottom-0 right-0 left-0 opacity-0 w-full h-full cursor-pointer"
|
||||
:disabled="!!defaultValue"
|
||||
@change="onCountrySelect(countries.find((country) => country.flag === $event.target.value))"
|
||||
>
|
||||
<option
|
||||
v-for="(country, index) in countries"
|
||||
:key="index"
|
||||
:value="country.flag"
|
||||
>
|
||||
{{ country.flag }} {{ country.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<input
|
||||
:name="`values[${field.uuid}]`"
|
||||
:value="fullInternationalPhoneValue"
|
||||
hidden
|
||||
>
|
||||
<input
|
||||
:id="field.uuid"
|
||||
ref="phone"
|
||||
:value="phoneValue"
|
||||
:readonly="!!defaultValue"
|
||||
class="base-input !text-2xl !rounded-l-none !border-l-0 !outline-none w-full"
|
||||
autocomplete="tel"
|
||||
type="tel"
|
||||
inputmode="tel"
|
||||
:required="field.required"
|
||||
placeholder="234 567-8900"
|
||||
@input="onPhoneInput"
|
||||
@focus="$emit('focus')"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MarkdownContent from './markdown_content'
|
||||
import phoneData from './phone_data'
|
||||
|
||||
function throttle (func, delay) {
|
||||
let lastCallTime = 0
|
||||
@@ -144,13 +169,73 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
isCodeSent: false,
|
||||
isResendLoading: false
|
||||
isResendLoading: false,
|
||||
phoneValue: this.modelValue || this.defaultValue || '',
|
||||
selectedCountry: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
countries () {
|
||||
return phoneData.map(([name, dial, flag, tz]) => {
|
||||
return { name, dial, flag, tz }
|
||||
})
|
||||
},
|
||||
countriesDialIndex () {
|
||||
return this.countries.reduce((acc, item) => {
|
||||
acc[item.dial] ||= item
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
dialCodesRegexp () {
|
||||
const dialCodes = this.countries.map((country) => country.dial).sort((a, b) => b.length - a.length)
|
||||
|
||||
return new RegExp(`^\\+(${dialCodes.join('|')})`)
|
||||
},
|
||||
detectedPhoneValueDialCode () {
|
||||
return (this.phoneValue || '').replace(/[^\d+]/g, '').match(this.dialCodesRegexp)?.[1]
|
||||
},
|
||||
fullInternationalPhoneValue () {
|
||||
if (this.detectedPhoneValueDialCode) {
|
||||
return this.phoneValue
|
||||
} else if (this.phoneValue) {
|
||||
return ['+', this.selectedCountry.dial, this.phoneValue].filter(Boolean).join('')
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const browserTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
|
||||
if (this.detectedPhoneValueDialCode) {
|
||||
this.selectedCountry = this.countriesDialIndex[this.detectedPhoneValueDialCode]
|
||||
} else if (browserTimeZone) {
|
||||
const tz = browserTimeZone.split('/')[1]
|
||||
|
||||
this.selectedCountry = this.countries.find((country) => country.tz.includes(tz)) || this.countries[0]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitSubmit: throttle(function (e) {
|
||||
this.$emit('submit')
|
||||
}, 1000),
|
||||
onCountrySelect (country) {
|
||||
if (this.selectedCountry.flag !== country.flag) {
|
||||
this.phoneValue = this.phoneValue.replace(`+${this.selectedCountry.dial}`, `+${country.dial}`)
|
||||
}
|
||||
|
||||
this.selectedCountry = country
|
||||
|
||||
this.$refs.phone.focus()
|
||||
},
|
||||
onPhoneInput (e) {
|
||||
this.phoneValue = e.target.value
|
||||
|
||||
if (this.detectedPhoneValueDialCode) {
|
||||
this.selectedCountry = this.countriesDialIndex[this.detectedPhoneValueDialCode]
|
||||
}
|
||||
},
|
||||
onInputCode (e) {
|
||||
if (e.target.value.length === 6) {
|
||||
this.emitSubmit()
|
||||
@@ -171,14 +256,14 @@ export default {
|
||||
body: JSON.stringify({
|
||||
submitter_slug: this.submitterSlug,
|
||||
locale: this.locale,
|
||||
phone: this.$refs.phone.value
|
||||
phone: this.fullInternationalPhoneValue
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}).then(async (resp) => {
|
||||
if (resp.status === 422) {
|
||||
const data = await resp.json()
|
||||
|
||||
alert(this.t('number_phone_is_invalid').replace('{number}', this.$refs.phone.value))
|
||||
alert(this.t('number_phone_is_invalid').replace('{number}', this.fullInternationalPhoneValue))
|
||||
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
@@ -189,13 +274,13 @@ export default {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
if (!this.$refs.phone.value.toString().startsWith('+')) {
|
||||
if (!this.fullInternationalPhoneValue.toString().startsWith('+')) {
|
||||
alert(this.t('use_international_format'))
|
||||
|
||||
return Promise.reject(new Error('phone invalid'))
|
||||
} else if (!this.isCodeSent) {
|
||||
return this.sendVerificationCode().then(() => {
|
||||
this.$emit('update:model-value', this.$refs.phone.value)
|
||||
this.$emit('update:model-value', this.fullInternationalPhoneValue)
|
||||
|
||||
this.isCodeSent = true
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@
|
||||
<select
|
||||
v-if="requireSigningReason && !isOtherReason"
|
||||
class="select base-input !text-2xl w-full mt-6 text-center"
|
||||
:class="{ 'text-gray-300': !reason }"
|
||||
required
|
||||
:name="`values[${field.preferences.reason_field_uuid}]`"
|
||||
@change="$event.target.value === 'other' ? [reason = '', isOtherReason = true] : $emit('update:reason', $event.target.value)"
|
||||
@@ -205,6 +206,7 @@
|
||||
value=""
|
||||
disabled
|
||||
:selected="!reason"
|
||||
class="text-gray-300"
|
||||
>
|
||||
{{ t('select_a_reason') }}
|
||||
</option>
|
||||
@@ -213,10 +215,14 @@
|
||||
:key="option"
|
||||
:value="option"
|
||||
:selected="reason === option"
|
||||
class="text-base-content"
|
||||
>
|
||||
{{ label }}
|
||||
</option>
|
||||
<option value="other">
|
||||
<option
|
||||
value="other"
|
||||
class="text-base-content"
|
||||
>
|
||||
{{ t('other') }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
@@ -360,6 +360,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
fieldNames: FieldType.computed.fieldNames,
|
||||
fieldLabels: FieldType.computed.fieldLabels,
|
||||
fieldIcons: FieldType.computed.fieldIcons,
|
||||
isDefaultValuePresent () {
|
||||
if (this.field?.type === 'radio' && this.field?.areas?.length > 1) {
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
>
|
||||
<div class="bg-base-200 rounded-lg p-5 text-center space-y-4">
|
||||
<p>
|
||||
{{ t('draw_field_on_the_document').replace('{field}', drawField?.name || '') }}
|
||||
{{ t('draw_field_on_the_document') }}
|
||||
</p>
|
||||
<div>
|
||||
<button
|
||||
@@ -418,7 +418,7 @@ import MobileFields from './mobile_fields'
|
||||
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments } from '@tabler/icons-vue'
|
||||
import { v4 } from 'uuid'
|
||||
import { ref, computed } from 'vue'
|
||||
import { en as i18nEn } from './i18n'
|
||||
import * as i18n from './i18n'
|
||||
|
||||
export default {
|
||||
name: 'TemplateBuilder',
|
||||
@@ -679,6 +679,9 @@ export default {
|
||||
computed: {
|
||||
selectedAreaRef: () => ref(),
|
||||
fieldsDragFieldRef: () => ref(),
|
||||
language () {
|
||||
return this.locale.split('-')[0].toLowerCase()
|
||||
},
|
||||
defaultDateFormat () {
|
||||
const isUsBrowser = Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US')
|
||||
const isUsTimezone = new Intl.DateTimeFormat('en-US', { timeZoneName: 'short' }).format(new Date()).match(/\s(?:CST|CDT|PST|PDT|EST|EDT)$/)
|
||||
@@ -690,6 +693,7 @@ export default {
|
||||
'de-DE': 'DD.MM.YYYY',
|
||||
'fr-FR': 'DD/MM/YYYY',
|
||||
'it-IT': 'DD/MM/YYYY',
|
||||
'en-GB': 'DD/MM/YYYY',
|
||||
'es-ES': 'DD/MM/YYYY'
|
||||
}
|
||||
},
|
||||
@@ -786,7 +790,7 @@ export default {
|
||||
document.activeElement.blur()
|
||||
},
|
||||
t (key) {
|
||||
return this.i18n[key] || i18nEn[key] || key
|
||||
return this.i18n[key] || i18n[this.language]?.[key] || i18n.en[key] || key
|
||||
},
|
||||
removePendingFields () {
|
||||
this.template.fields = this.template.fields.filter((f) => {
|
||||
@@ -1352,7 +1356,7 @@ export default {
|
||||
this.save()
|
||||
},
|
||||
onDocumentRemove (item) {
|
||||
if (window.confirm(this.t('are_you_sure'))) {
|
||||
if (window.confirm(this.t('are_you_sure_'))) {
|
||||
this.template.schema.splice(this.template.schema.indexOf(item), 1)
|
||||
}
|
||||
|
||||
@@ -1451,7 +1455,7 @@ export default {
|
||||
if (!this.template.fields.length) {
|
||||
e.preventDefault()
|
||||
|
||||
alert('Please draw fields to prepare the document.')
|
||||
alert(this.t('please_draw_fields_to_prepare_the_document'))
|
||||
} else {
|
||||
const submitterWithoutFields =
|
||||
this.template.submitters.find((submitter) => !this.template.fields.some((f) => f.submitter_uuid === submitter.uuid))
|
||||
@@ -1459,7 +1463,7 @@ export default {
|
||||
if (submitterWithoutFields) {
|
||||
e.preventDefault()
|
||||
|
||||
alert(`Please add fields for the ${submitterWithoutFields.name}. Or, remove the ${submitterWithoutFields.name} if not needed.`)
|
||||
alert(this.t('please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed').replaceAll('{submitter_name}', submitterWithoutFields.name))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1475,13 +1479,13 @@ export default {
|
||||
}
|
||||
|
||||
if (!this.template.fields.length) {
|
||||
alert('Please draw fields to prepare the document.')
|
||||
alert(this.t('please_draw_fields_to_prepare_the_document'))
|
||||
} else {
|
||||
const submitterWithoutFields =
|
||||
this.template.submitters.find((submitter) => !this.template.fields.some((f) => f.submitter_uuid === submitter.uuid))
|
||||
|
||||
if (submitterWithoutFields) {
|
||||
alert(`Please add fields for the ${submitterWithoutFields.name}. Or, remove the ${submitterWithoutFields.name} if not needed.`)
|
||||
alert(this.t('please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed').replaceAll('{submitter_name}', submitterWithoutFields.name))
|
||||
} else {
|
||||
this.isSaving = true
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
</div>
|
||||
<select
|
||||
class="select select-bordered select-sm w-full bg-white h-11 pl-4 text-base font-normal"
|
||||
:class="{ 'text-gray-300': !condition.field_uuid }"
|
||||
required
|
||||
@change="[
|
||||
condition.field_uuid = $event.target.value,
|
||||
@@ -68,6 +69,7 @@
|
||||
v-for="f in fields"
|
||||
:key="f.uuid"
|
||||
:value="f.uuid"
|
||||
class="text-base-content"
|
||||
:selected="condition.field_uuid === f.uuid"
|
||||
>
|
||||
{{ f.name || buildDefaultName(f, template.fields) }}
|
||||
@@ -88,8 +90,9 @@
|
||||
</option>
|
||||
</select>
|
||||
<select
|
||||
v-if="conditionField(condition)?.options?.length"
|
||||
v-if="['radio', 'select', 'multiple'].includes(conditionField(condition)?.type) && conditionField(condition)?.options"
|
||||
class="select select-bordered select-sm w-full bg-white h-11 pl-4 text-base font-normal"
|
||||
:class="{ 'text-gray-300': !condition.value }"
|
||||
required
|
||||
@change="condition.value = $event.target.value"
|
||||
>
|
||||
@@ -105,6 +108,7 @@
|
||||
:key="option.uuid"
|
||||
:value="option.uuid"
|
||||
:selected="condition.value === option.uuid"
|
||||
class="text-base-content"
|
||||
>
|
||||
{{ option.value || `${t('option')} ${index + 1}` }}
|
||||
</option>
|
||||
@@ -200,7 +204,7 @@ export default {
|
||||
},
|
||||
validateSaveAndClose () {
|
||||
if (!this.withConditions) {
|
||||
return alert('Available only in Pro')
|
||||
return alert(this.t('available_only_in_pro'))
|
||||
}
|
||||
|
||||
if (this.conditions.find((f) => f.field_uuid)) {
|
||||
|
||||
@@ -105,7 +105,7 @@ export default {
|
||||
|
||||
this.upload()
|
||||
} else {
|
||||
alert('Only PDF and images are supported.')
|
||||
alert(this.t('only_pdf_and_images_are_supported'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,6 +312,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
fieldNames: FieldType.computed.fieldNames,
|
||||
fieldLabels: FieldType.computed.fieldLabels,
|
||||
dropdownBgColor () {
|
||||
return ['', null, 'transparent'].includes(this.backgroundColor) ? 'white' : this.backgroundColor
|
||||
},
|
||||
@@ -367,8 +368,7 @@ export default {
|
||||
if (this.field.type === 'heading') {
|
||||
return `${this.fieldNames[field.type]} ${typeIndex + 1}`
|
||||
} else {
|
||||
const suffix = { multiple: this.t('select'), radio: this.t('group') }[field.type] || this.t('field')
|
||||
return `${this.fieldNames[field.type]} ${suffix} ${typeIndex + 1}`
|
||||
return `${this.fieldLabels[field.type]} ${typeIndex + 1}`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -330,7 +330,7 @@ export default {
|
||||
this.$emit('update:model-value', submitter.uuid)
|
||||
},
|
||||
remove (submitter) {
|
||||
if (window.confirm(this.t('are_you_sure'))) {
|
||||
if (window.confirm(this.t('are_you_sure_'))) {
|
||||
this.$emit('remove', submitter)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -114,6 +114,25 @@ export default {
|
||||
phone: this.t('phone')
|
||||
}
|
||||
},
|
||||
fieldLabels () {
|
||||
return {
|
||||
text: this.t('text_field'),
|
||||
signature: this.t('signature_field'),
|
||||
initials: this.t('initials_field'),
|
||||
date: this.t('date_field'),
|
||||
number: this.t('number_field'),
|
||||
image: this.t('image_field'),
|
||||
file: this.t('file_field'),
|
||||
select: this.t('select_field'),
|
||||
checkbox: this.t('checkbox_field'),
|
||||
multiple: this.t('multiple_field'),
|
||||
radio: this.t('radio_field'),
|
||||
cells: this.t('cells_field'),
|
||||
stamp: this.t('stamp_field'),
|
||||
payment: this.t('payment_field'),
|
||||
phone: this.t('phone_field')
|
||||
}
|
||||
},
|
||||
fieldIcons () {
|
||||
return {
|
||||
heading: IconHeading,
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
v-else-if="type == 'phone' && (fieldTypes.length === 0 || fieldTypes.includes(type))"
|
||||
class="tooltip tooltip-bottom flex"
|
||||
:class="{'tooltip-bottom-end': withPayment, 'tooltip-bottom': !withPayment }"
|
||||
data-tip="Unlock SMS-verified phone number field with paid plan. Use text field for phone numbers without verification."
|
||||
:data-tip="t('unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification')"
|
||||
>
|
||||
<a
|
||||
href="https://www.docuseal.co/pricing"
|
||||
@@ -168,13 +168,13 @@
|
||||
>
|
||||
<ul class="list-disc list-outside ml-3">
|
||||
<li>
|
||||
Draw a text field on the page with a mouse
|
||||
{{ t('draw_a_text_field_on_the_page_with_a_mouse') }}
|
||||
</li>
|
||||
<li>
|
||||
Drag & drop any other field type on the page
|
||||
{{ t('drag_and_drop_any_other_field_type_on_the_page') }}
|
||||
</li>
|
||||
<li>
|
||||
Click on the field type above to start drawing it
|
||||
{{ t('click_on_the_field_type_above_to_start_drawing_it') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -195,7 +195,7 @@ export default {
|
||||
},
|
||||
validateSaveAndClose () {
|
||||
if (!this.withFormula) {
|
||||
return alert('Available only in Pro')
|
||||
return alert(this.t('available_only_in_pro'))
|
||||
}
|
||||
|
||||
const normalizedFormula = this.normalizeFormula(this.formula)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const en = {
|
||||
editable: 'Editable',
|
||||
recurrent: 'Recurrent',
|
||||
one_off: 'One-off',
|
||||
search_field: 'Search field',
|
||||
field_not_found: 'Field not found',
|
||||
clear: 'Clear',
|
||||
@@ -29,7 +31,7 @@ const en = {
|
||||
select_value_: 'Select value...',
|
||||
remove_condition: 'Remove condition',
|
||||
add_condition: 'Add condition',
|
||||
are_you_sure: 'Are you sure?',
|
||||
are_you_sure_: 'Are you sure?',
|
||||
sign_yourself: 'Sign Yourself',
|
||||
set_signing_date: 'Set signing date',
|
||||
signing_date: 'Signing Date',
|
||||
@@ -46,7 +48,7 @@ const en = {
|
||||
drawn: 'Drawn',
|
||||
formula: 'Formula',
|
||||
typed: 'Typed',
|
||||
draw_field_on_the_document: 'Draw {field} field on the document',
|
||||
draw_field_on_the_document: 'Draw a field on the document',
|
||||
click_to_upload: 'Click to upload',
|
||||
or_drag_and_drop_files: 'or drag and drop files',
|
||||
uploading: 'Uploading',
|
||||
@@ -101,9 +103,31 @@ const en = {
|
||||
stamp: 'Stamp',
|
||||
payment: 'Payment',
|
||||
phone: 'Phone',
|
||||
field: 'Field',
|
||||
group: 'Group',
|
||||
text_field: 'Text Field',
|
||||
signature_field: 'Signature Field',
|
||||
initials_field: 'Initials Field',
|
||||
date_field: 'Date Field',
|
||||
number_field: 'Number Field',
|
||||
image_field: 'Image Field',
|
||||
file_field: 'File Field',
|
||||
select_field: 'Select Field',
|
||||
checkbox_field: 'Checkbox Field',
|
||||
multiple_field: 'Multiple Select Field',
|
||||
radio_field: 'Radio Group Field',
|
||||
cells_field: 'Cells Field',
|
||||
stamp_field: 'Stamp Field',
|
||||
payment_field: 'Payment Field',
|
||||
phone_field: 'Phone Field',
|
||||
draw_a_text_field_on_the_page_with_a_mouse: 'Draw a text field on the page with a mouse',
|
||||
drag_and_drop_any_other_field_type_on_the_page: 'Drag & drop any other field type on the page',
|
||||
click_on_the_field_type_above_to_start_drawing_it: 'Click on the field type above to start drawing it',
|
||||
please_draw_fields_to_prepare_the_document: 'Please draw fields to prepare the document.',
|
||||
only_pdf_and_images_are_supported: 'Only PDF and images are supported.',
|
||||
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: 'Unlock SMS-verified phone number field with paid plan. Use text field for phone numbers without verification.',
|
||||
available_only_in_pro: 'Available only in Pro',
|
||||
failed_to_download_files: 'Failed to download files',
|
||||
signature_is_too_small_please_redraw: 'Signature is too small - please redraw.',
|
||||
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: 'Please add fields for the {submitter_name}. Or, remove the {submitter_name} if not needed.',
|
||||
draw_field: 'Draw {field} Field',
|
||||
replace: 'Replace',
|
||||
uploading_: 'Uploading...',
|
||||
@@ -128,4 +152,774 @@ const en = {
|
||||
learn_more: 'Learn more'
|
||||
}
|
||||
|
||||
export { en }
|
||||
const es = {
|
||||
recurrent: 'Recurrente',
|
||||
one_off: 'Único',
|
||||
editable: 'Editable',
|
||||
search_field: 'Campo de búsqueda',
|
||||
field_not_found: 'Campo no encontrado',
|
||||
clear: 'Borrar',
|
||||
type_value: 'Escriba valor',
|
||||
align: 'Alinear',
|
||||
add_all_required_fields_to_continue: 'Agregar todos los campos requeridos para continuar',
|
||||
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'El PDF cargado tiene campos. ¿Mantenerlos o eliminarlos?',
|
||||
keep: 'Mantener',
|
||||
left: 'Izquierda',
|
||||
heading: 'Encabezado',
|
||||
validation: 'Validación',
|
||||
add_blank_page: 'Agregar página en blanco',
|
||||
right: 'Derecha',
|
||||
center: 'Centro',
|
||||
with_logo: 'Con logotipo',
|
||||
description: 'Descripción',
|
||||
signing_date: 'Fecha de Firma',
|
||||
display_title: 'Título de visualización',
|
||||
unchecked: 'No marcado',
|
||||
price: 'Precio',
|
||||
equal: 'Igual',
|
||||
not_equal: 'No es igual',
|
||||
contains: 'Contiene',
|
||||
does_not_contain: 'No contiene',
|
||||
not_empty: 'No vacío',
|
||||
empty: 'Vacío',
|
||||
select_field_: 'Seleccionar campo...',
|
||||
select_value_: 'Seleccionar valor...',
|
||||
remove_condition: 'Eliminar condición',
|
||||
add_condition: 'Agregar condición',
|
||||
condition: 'Condición',
|
||||
formula: 'Fórmula',
|
||||
edit: 'Editar',
|
||||
settings: 'Configuración',
|
||||
up: 'Arriba',
|
||||
down: 'Abajo',
|
||||
set_signing_date: 'Establecer fecha de firma',
|
||||
are_you_sure_: '¿Estás seguro?',
|
||||
sign_yourself: 'Firma tú mismo',
|
||||
checked: 'Seleccionado',
|
||||
send: 'Enviar',
|
||||
remove: 'Eliminar',
|
||||
save: 'Guardar',
|
||||
cancel: 'Cancelar',
|
||||
or_add_field_without_drawing: 'O añadir campo sin dibujar',
|
||||
draw_field_on_the_document: 'Dibujar un campo en el documento',
|
||||
click_to_upload: 'Haz clic para cargar',
|
||||
or_drag_and_drop_files: 'o arrastra y suelta archivos',
|
||||
uploading: 'Subiendo',
|
||||
processing_: 'Procesando...',
|
||||
add_pdf_documents_or_images: 'Agregar documentos PDF o imágenes',
|
||||
add_documents_or_images: 'Agregar documentos o imágenes',
|
||||
required: 'Requerido',
|
||||
default_value: 'Valor predeterminado',
|
||||
format: 'Formato',
|
||||
read_only: 'Solo lectura',
|
||||
page: 'Página',
|
||||
draw_new_area: 'Dibujar nueva área',
|
||||
copy_to_all_pages: 'Copiar a todas las páginas',
|
||||
add_option: 'Agregar opción',
|
||||
option: 'Opción',
|
||||
first_party: 'Primera Parte',
|
||||
second_party: 'Segunda Parte',
|
||||
third_party: 'Tercera Parte',
|
||||
fourth_party: 'Cuarta Parte',
|
||||
fifth_party: 'Quinta Parte',
|
||||
sixth_party: 'Sexta Parte',
|
||||
seventh_party: 'Séptima Parte',
|
||||
eighth_party: 'Octava Parte',
|
||||
ninth_party: 'Novena Parte',
|
||||
tenth_party: 'Décima Parte',
|
||||
eleventh_party: 'Undécimo Partido',
|
||||
twelfth_party: 'Duodécimo Partido',
|
||||
thirteenth_party: 'Decimotercer Partido',
|
||||
fourteenth_party: 'Catorceavo Partido',
|
||||
fifteenth_party: 'Quinceavo Partido',
|
||||
sixteenth_party: 'Dieciséisavo Partido',
|
||||
seventeenth_party: 'Diecisieteavo Partido',
|
||||
eighteenth_party: 'Dieciochoavo Partido',
|
||||
nineteenth_party: 'Decimonovena Fiesta',
|
||||
twentieth_party: 'Vigésima Fiesta',
|
||||
draw: 'Dibujar',
|
||||
add: 'Agregar',
|
||||
text: 'Texto',
|
||||
signature: 'Firma',
|
||||
initials: 'Iniciales',
|
||||
date: 'Fecha',
|
||||
number: 'Número',
|
||||
image: 'Imagen',
|
||||
file: 'Archivo',
|
||||
select: 'Seleccionar',
|
||||
checkbox: 'Casilla',
|
||||
multiple: 'Múltiple',
|
||||
radio: 'Radio',
|
||||
cells: 'Celdas',
|
||||
stamp: 'Sello',
|
||||
payment: 'Pago',
|
||||
phone: 'Teléfono',
|
||||
text_field: 'Campo de Texto',
|
||||
signature_field: 'Campo de Firma',
|
||||
initials_field: 'Campo de Iniciales',
|
||||
date_field: 'Campo de Fecha',
|
||||
number_field: 'Campo de Número',
|
||||
image_field: 'Campo de Imagen',
|
||||
file_field: 'Campo de Archivo',
|
||||
select_field: 'Campo de Selección',
|
||||
checkbox_field: 'Campo de Casilla',
|
||||
multiple_field: 'Campo Múltiple',
|
||||
radio_field: 'Campo de Grupo Radio',
|
||||
cells_field: 'Campo de Celdas',
|
||||
stamp_field: 'Campo de Sello',
|
||||
payment_field: 'Campo de Pago',
|
||||
phone_field: 'Campo de Teléfono',
|
||||
draw_a_text_field_on_the_page_with_a_mouse: 'Dibujar un campo de texto en la página con el mouse',
|
||||
drag_and_drop_any_other_field_type_on_the_page: 'Arrastra y suelta cualquier otro tipo de campo en la página',
|
||||
click_on_the_field_type_above_to_start_drawing_it: 'Haz clic en el tipo de campo de arriba para comenzar a dibujarlo',
|
||||
please_draw_fields_to_prepare_the_document: 'Por favor, dibuja los campos para preparar el documento.',
|
||||
only_pdf_and_images_are_supported: 'Solo se admiten PDF e imágenes.',
|
||||
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: 'Desbloquea el campo de número de teléfono verificado por SMS con un plan pago. Usa el campo de texto para números de teléfono sin verificación.',
|
||||
available_only_in_pro: 'Disponible solo en Pro',
|
||||
failed_to_download_files: 'Error al descargar los archivos',
|
||||
signature_is_too_small_please_redraw: 'La firma es demasiado pequeña. Por favor, dibújala de nuevo.',
|
||||
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: 'Por favor, añade campos para {submitter_name} o elimina {submitter_name} si no es necesario.',
|
||||
draw_field: 'Dibujar campo {field}',
|
||||
replace: 'Reemplazar',
|
||||
uploading_: 'Subiendo...',
|
||||
add_document: 'Subir',
|
||||
any: 'Cualquier',
|
||||
drawn: 'Dibujado',
|
||||
typed: 'Escrito',
|
||||
none: 'Ninguno',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
email: 'Correo electrónico',
|
||||
url: 'URL',
|
||||
zip: 'ZIP',
|
||||
custom: 'Personalizado',
|
||||
numbers_only: 'Solo números',
|
||||
letters_only: 'Solo letras',
|
||||
regexp_validation: 'Validación de expresión regular',
|
||||
enter_pdf_password: 'Ingrese la contraseña del PDF',
|
||||
wrong_password: 'Contraseña incorrecta',
|
||||
currency: 'Moneda',
|
||||
save_and_preview: 'Guardar y previsualizar',
|
||||
preferences: 'Preferencias',
|
||||
available_in_pro: 'Disponible en Pro',
|
||||
some_fields_are_missing_in_the_formula: 'Faltan algunos campos en la fórmula.',
|
||||
learn_more: 'Aprende más'
|
||||
}
|
||||
|
||||
const it = {
|
||||
ricorrente: 'Ricorrente',
|
||||
una_volta: 'Una volta',
|
||||
editable: 'Modificabile',
|
||||
search_field: 'Campo di ricerca',
|
||||
field_not_found: 'Campo non trovato',
|
||||
clear: 'Cancella',
|
||||
align: 'Allinea',
|
||||
add_all_required_fields_to_continue: 'Aggiungi tutti i campi obbligatori per continuare',
|
||||
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Il PDF caricato contiene campi del modulo. Mantenerli o rimuoverli?',
|
||||
keep: 'Mantieni',
|
||||
left: 'Sinistra',
|
||||
heading: 'Intestazione',
|
||||
validation: 'Validazione',
|
||||
add_blank_page: 'Aggiungi pagina vuota',
|
||||
right: 'Destra',
|
||||
center: 'Centro',
|
||||
description: 'Descrizione',
|
||||
display_title: 'Mostra titolo',
|
||||
with_logo: 'Con logo',
|
||||
unchecked: 'Non selezionato',
|
||||
price: 'Prezzo',
|
||||
type_value: 'Inserisci valore',
|
||||
equal: 'Uguale',
|
||||
not_equal: 'Non uguale',
|
||||
contains: 'Contiene',
|
||||
does_not_contain: 'Non contiene',
|
||||
not_empty: 'Non vuoto',
|
||||
empty: 'Vuoto',
|
||||
select_field_: 'Seleziona campo...',
|
||||
select_value_: 'Seleziona valore...',
|
||||
remove_condition: 'Rimuovi condizione',
|
||||
add_condition: 'Aggiungi condizione',
|
||||
are_you_sure_: 'Sei sicuro?',
|
||||
sign_yourself: 'Firma te stesso',
|
||||
set_signing_date: 'Imposta data di firma',
|
||||
signing_date: 'Data di firma',
|
||||
send: 'Invia',
|
||||
remove: 'Rimuovi',
|
||||
edit: 'Modifica',
|
||||
settings: 'Impostazioni',
|
||||
up: 'Su',
|
||||
down: 'Giù',
|
||||
checked: 'Selezionato',
|
||||
save: 'Salva',
|
||||
cancel: 'Annulla',
|
||||
any: 'Qualsiasi',
|
||||
drawn: 'Disegnato',
|
||||
formula: 'Formula',
|
||||
typed: 'Digitato',
|
||||
draw_field_on_the_document: 'Disegnare un campo sul documento',
|
||||
click_to_upload: 'Clicca per caricare',
|
||||
or_drag_and_drop_files: 'o trascina e rilascia i file',
|
||||
uploading: 'Caricamento in corso',
|
||||
processing_: 'Elaborazione...',
|
||||
add_pdf_documents_or_images: 'Aggiungi documenti PDF o immagini',
|
||||
add_documents_or_images: 'Aggiungi documenti o immagini',
|
||||
required: 'Obbligatorio',
|
||||
default_value: 'Valore predefinito',
|
||||
format: 'Formato',
|
||||
read_only: 'Sola lettura',
|
||||
page: 'Pagina',
|
||||
draw_new_area: 'Disegna nuova area',
|
||||
copy_to_all_pages: 'Copia in tutte le pagine',
|
||||
add_option: 'Aggiungi opzione',
|
||||
option: 'Opzione',
|
||||
condition: 'Condizione',
|
||||
first_party: 'Prima parte',
|
||||
second_party: 'Seconda parte',
|
||||
third_party: 'Terza parte',
|
||||
fourth_party: 'Quarta parte',
|
||||
fifth_party: 'Quinta parte',
|
||||
sixth_party: 'Sesta parte',
|
||||
seventh_party: 'Settima parte',
|
||||
eighth_party: 'Ottava parte',
|
||||
ninth_party: 'Nona parte',
|
||||
tenth_party: 'Decima parte',
|
||||
eleventh_party: 'Undicesima parte',
|
||||
twelfth_party: 'Dodicesima parte',
|
||||
thirteenth_party: 'Tredicesima parte',
|
||||
fourteenth_party: 'Quattordicesima parte',
|
||||
fifteenth_party: 'Quindicesima parte',
|
||||
sixteenth_party: 'Sedicesima parte',
|
||||
seventeenth_party: 'Diciassettesima parte',
|
||||
eighteenth_party: 'Diciottesima parte',
|
||||
nineteenth_party: 'Diciannovesima parte',
|
||||
twentieth_party: 'Ventesima parte',
|
||||
draw: 'Disegna',
|
||||
add: 'Aggiungi',
|
||||
or_add_field_without_drawing: 'Oppure aggiungi campo senza disegno',
|
||||
text: 'Testo',
|
||||
number: 'Numero',
|
||||
signature: 'Firma',
|
||||
initials: 'Iniziali',
|
||||
date: 'Data',
|
||||
image: 'Immagine',
|
||||
file: 'File',
|
||||
select: 'Seleziona',
|
||||
checkbox: 'Checkbox',
|
||||
multiple: 'Multiplo',
|
||||
radio: 'Radio',
|
||||
cells: 'Celle',
|
||||
stamp: 'Timbro',
|
||||
payment: 'Pagamento',
|
||||
phone: 'Telefono',
|
||||
text_field: 'Campo di Testo',
|
||||
signature_field: 'Campo di Firma',
|
||||
initials_field: 'Campo di Iniziali',
|
||||
date_field: 'Campo Data',
|
||||
number_field: 'Campo Numero',
|
||||
image_field: 'Campo Immagine',
|
||||
file_field: 'Campo File',
|
||||
select_field: 'Campo di Selezione',
|
||||
checkbox_field: 'Campo di Checkbox',
|
||||
multiple_field: 'Campo Selezione Multipla',
|
||||
radio_field: 'Campo di Gruppo Radio',
|
||||
cells_field: 'Campo Celle',
|
||||
stamp_field: 'Campo Timbro',
|
||||
payment_field: 'Campo Pagamento',
|
||||
phone_field: 'Campo Telefono',
|
||||
draw_a_text_field_on_the_page_with_a_mouse: 'Disegna un campo di testo sulla pagina con il mouse',
|
||||
drag_and_drop_any_other_field_type_on_the_page: 'Trascina e rilascia qualsiasi altro tipo di campo sulla pagina',
|
||||
click_on_the_field_type_above_to_start_drawing_it: 'Clicca sul tipo di campo sopra per iniziare a disegnarlo',
|
||||
please_draw_fields_to_prepare_the_document: 'Per favore, disegna i campi per preparare il documento.',
|
||||
only_pdf_and_images_are_supported: 'Sono supportati solo PDF e immagini.',
|
||||
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: 'Sblocca il campo numero di telefono verificato tramite SMS con un piano a pagamento. Usa il campo di testo per numeri di telefono senza verifica.',
|
||||
available_only_in_pro: 'Disponibile solo in Pro',
|
||||
failed_to_download_files: 'Impossibile scaricare i file',
|
||||
signature_is_too_small_please_redraw: 'La firma è troppo piccola. Ridisegnala per favore.',
|
||||
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: 'Aggiungi campi per {submitter_name} o rimuovi {submitter_name} se non necessario.',
|
||||
draw_field: 'Disegna il campo {field}',
|
||||
replace: 'Sostituisci',
|
||||
uploading_: 'Caricamento in corso...',
|
||||
add_document: 'Aggiungi',
|
||||
none: 'Nessuno',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
email: 'Email',
|
||||
url: 'URL',
|
||||
zip: 'CAP',
|
||||
custom: 'Personalizzato',
|
||||
numbers_only: 'Solo numeri',
|
||||
letters_only: 'Solo lettere',
|
||||
regexp_validation: 'Validazione regexp',
|
||||
enter_pdf_password: 'Inserisci password PDF',
|
||||
wrong_password: 'Password errata',
|
||||
currency: 'Valuta',
|
||||
save_and_preview: 'Salva e Anteprima',
|
||||
preferences: 'Preferenze',
|
||||
available_in_pro: 'Disponibile in Pro',
|
||||
some_fields_are_missing_in_the_formula: 'Alcuni campi mancano nella formula.',
|
||||
learn_more: 'Scopri di più'
|
||||
}
|
||||
|
||||
const pt = {
|
||||
recurrent: 'Recurrente',
|
||||
one_off: 'Único',
|
||||
editable: 'Editável',
|
||||
search_field: 'Campo de busca',
|
||||
field_not_found: 'Campo não encontrado',
|
||||
clear: 'Limpar',
|
||||
type_value: 'Digite valor',
|
||||
add_all_required_fields_to_continue: 'Adicione todos os campos obrigatórios para continuar',
|
||||
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'O PDF carregado contém campos. Manter ou removê-los?',
|
||||
keep: 'Manter',
|
||||
align: 'Alinhar',
|
||||
left: 'Esquerda',
|
||||
heading: 'Cabeçalho',
|
||||
validation: 'Validação',
|
||||
add_blank_page: 'Adicionar página em branco',
|
||||
right: 'Direita',
|
||||
center: 'Centro',
|
||||
with_logo: 'Com logotipo',
|
||||
description: 'Descrição',
|
||||
display_title: 'Título de exibição',
|
||||
signing_date: 'Data da Assinatura',
|
||||
unchecked: 'Não marcado',
|
||||
price: 'Preço',
|
||||
equal: 'Igual',
|
||||
not_equal: 'Não é igual',
|
||||
contains: 'Contém',
|
||||
does_not_contain: 'Não contém',
|
||||
not_empty: 'Não vazio',
|
||||
empty: 'Vazio',
|
||||
add_condition: 'Adicionar condição',
|
||||
select_field_: 'Selecionar campo...',
|
||||
select_value_: 'Selecionar valor...',
|
||||
remove_condition: 'Remover condição',
|
||||
condition: 'Condição',
|
||||
formula: 'Fórmula',
|
||||
edit: 'Editar',
|
||||
settings: 'Configurações',
|
||||
up: 'Para cima',
|
||||
down: 'Para baixo',
|
||||
set_signing_date: 'Definir data de assinatura',
|
||||
are_you_sure_: 'Tem certeza?',
|
||||
sign_yourself: 'Assine você mesmo',
|
||||
checked: 'Marcado',
|
||||
send: 'Enviar',
|
||||
remove: 'Remover',
|
||||
save: 'Salvar',
|
||||
cancel: 'Cancelar',
|
||||
or_add_field_without_drawing: 'Ou adicione campo sem desenhar',
|
||||
draw_field_on_the_document: 'Desenhar um campo no documento',
|
||||
click_to_upload: 'Clique para enviar',
|
||||
or_drag_and_drop_files: 'ou arraste e solte arquivos',
|
||||
uploading: 'Carregando',
|
||||
processing_: 'Processando...',
|
||||
add_pdf_documents_or_images: 'Adicionar documentos PDF ou imagens',
|
||||
add_documents_or_images: 'Adicionar documentos ou imagens',
|
||||
required: 'Obrigatório',
|
||||
default_value: 'Valor padrão',
|
||||
format: 'Formato',
|
||||
read_only: 'Somente leitura',
|
||||
page: 'Página',
|
||||
draw_new_area: 'Desenhar nova área',
|
||||
copy_to_all_pages: 'Copiar para todas as páginas',
|
||||
add_option: 'Adicionar opção',
|
||||
option: 'Opção',
|
||||
first_party: 'Primeira Parte',
|
||||
second_party: 'Segunda Parte',
|
||||
third_party: 'Terceira Parte',
|
||||
fourth_party: 'Quarta Parte',
|
||||
fifth_party: 'Quinta Parte',
|
||||
sixth_party: 'Sexta Parte',
|
||||
seventh_party: 'Sétima Parte',
|
||||
eighth_party: 'Oitava Parte',
|
||||
ninth_party: 'Nona Parte',
|
||||
tenth_party: 'Décima Parte',
|
||||
eleventh_party: 'Décima Primeira Parte',
|
||||
twelfth_party: 'Décima Segunda Parte',
|
||||
thirteenth_party: 'Décima Terceira Parte',
|
||||
fourteenth_party: 'Décima Quarta Parte',
|
||||
fifteenth_party: 'Décima Quinta Parte',
|
||||
sixteenth_party: 'Décima Sexta Parte',
|
||||
seventeenth_party: 'Décima Sétima Parte',
|
||||
eighteenth_party: 'Décima Oitava Parte',
|
||||
nineteenth_party: 'Décima Nona Parte',
|
||||
twentieth_party: 'Vigésima Parte',
|
||||
draw: 'Desenhar',
|
||||
add: 'Adicionar',
|
||||
text: 'Texto',
|
||||
signature: 'Assinatura',
|
||||
initials: 'Rúbrica',
|
||||
date: 'Data',
|
||||
number: 'Número',
|
||||
image: 'Imagem',
|
||||
file: 'Arquivo',
|
||||
select: 'Selecionar',
|
||||
checkbox: 'Caixa',
|
||||
multiple: 'Múltiplo',
|
||||
radio: 'Rádio',
|
||||
cells: 'Células',
|
||||
stamp: 'Carimbo',
|
||||
payment: 'Pagamento',
|
||||
phone: 'Telefone',
|
||||
text_field: 'Campo de Texto',
|
||||
signature_field: 'Campo de Assinatura',
|
||||
initials_field: 'Campo de Rúbrica',
|
||||
date_field: 'Campo de Data',
|
||||
number_field: 'Campo de Número',
|
||||
image_field: 'Campo de Imagem',
|
||||
file_field: 'Campo de Arquivo',
|
||||
select_field: 'Campo de Seleção',
|
||||
checkbox_field: 'Campo de Caixa',
|
||||
multiple_field: 'Campo de Opção Múltipla',
|
||||
radio_field: 'Campo de Grupo Rádio',
|
||||
cells_field: 'Campo de Células',
|
||||
stamp_field: 'Campo de Carimbo',
|
||||
payment_field: 'Campo de Pagamento',
|
||||
phone_field: 'Campo de Telefone',
|
||||
draw_a_text_field_on_the_page_with_a_mouse: 'Desenhar um campo de texto na página com o mouse',
|
||||
drag_and_drop_any_other_field_type_on_the_page: 'Arraste e solte qualquer outro tipo de campo na página',
|
||||
click_on_the_field_type_above_to_start_drawing_it: 'Clique no tipo de campo acima para começar a desenhá-lo',
|
||||
please_draw_fields_to_prepare_the_document: 'Por favor, desenhe os campos para preparar o documento.',
|
||||
only_pdf_and_images_are_supported: 'Apenas PDFs e imagens são suportados.',
|
||||
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: 'Desbloqueie o campo de número de telefone verificado por SMS com um plano pago. Use o campo de texto para números de telefone sem verificação.',
|
||||
available_only_in_pro: 'Disponível apenas no Pro',
|
||||
failed_to_download_files: 'Falha ao baixar arquivos',
|
||||
signature_is_too_small_please_redraw: 'A assinatura é muito pequena. Por favor, redesenhe.',
|
||||
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: 'Adicione campos para {submitter_name} ou remova {submitter_name} se não for necessário.',
|
||||
draw_field: 'Desenhar campo {field}',
|
||||
replace: 'Substituir',
|
||||
uploading_: 'Carregando...',
|
||||
add_document: 'Enviar',
|
||||
any: 'Qualquer',
|
||||
drawn: 'Desenhado',
|
||||
typed: 'Digitado',
|
||||
none: 'Nenhum',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
email: 'Email',
|
||||
url: 'URL',
|
||||
zip: 'ZIP',
|
||||
custom: 'Personalizado',
|
||||
numbers_only: 'Somente números',
|
||||
letters_only: 'Somente letras',
|
||||
regexp_validation: 'Validação de expressão regular',
|
||||
enter_pdf_password: 'Digite a senha do PDF',
|
||||
wrong_password: 'Senha incorreta',
|
||||
currency: 'Moeda',
|
||||
save_and_preview: 'Salvar e Pré-visualizar',
|
||||
preferences: 'Preferências',
|
||||
available_in_pro: 'Disponível no Pro',
|
||||
some_fields_are_missing_in_the_formula: 'Faltam alguns campos na fórmula.',
|
||||
learn_more: 'Saiba mais'
|
||||
}
|
||||
|
||||
const fr = {
|
||||
recurrent: 'Récurrent',
|
||||
one_off: 'Ponctuel',
|
||||
editable: 'Éditable',
|
||||
search_field: 'Champ de recherche',
|
||||
field_not_found: 'Champ non trouvé',
|
||||
clear: 'Effacer',
|
||||
type_value: 'Tapez la valeur',
|
||||
add_all_required_fields_to_continue: 'Ajoutez tous les champs obligatoires pour continuer',
|
||||
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Le PDF téléchargé contient des champs. Les conserver ou les supprimer?',
|
||||
keep: 'Conserver',
|
||||
align: 'Aligner',
|
||||
left: 'Gauche',
|
||||
heading: 'En-tête',
|
||||
validation: 'Validation',
|
||||
add_blank_page: 'Ajouter une page blanche',
|
||||
right: 'Droite',
|
||||
add_condition: 'Ajouter une condition',
|
||||
center: 'Centre',
|
||||
with_logo: 'Avec logo',
|
||||
description: 'Description',
|
||||
display_title: "Titre d'affichage",
|
||||
unchecked: 'Non coché',
|
||||
price: 'Prix',
|
||||
equal: 'Égal',
|
||||
not_equal: 'Non égal',
|
||||
contains: 'Contient',
|
||||
signing_date: 'Date de signature',
|
||||
does_not_contain: 'Ne contient pas',
|
||||
not_empty: 'Non vide',
|
||||
empty: 'Vide',
|
||||
select_field_: 'Sélectionner un champ...',
|
||||
select_value_: 'Sélectionner une valeur...',
|
||||
remove_condition: 'Supprimer la condition',
|
||||
condition: 'Condition',
|
||||
formula: 'Formule',
|
||||
edit: 'Éditer',
|
||||
settings: 'Paramètres',
|
||||
up: 'Haut',
|
||||
down: 'Bas',
|
||||
set_signing_date: 'Définir la date de signature',
|
||||
are_you_sure_: 'Êtes-vous sûr?',
|
||||
sign_yourself: 'Signez-vous',
|
||||
checked: 'Coché',
|
||||
send: 'Envoyer',
|
||||
remove: 'Supprimer',
|
||||
save: 'Enregistrer',
|
||||
cancel: 'Annuler',
|
||||
draw_field_on_the_document: 'Dessiner un champ sur le document',
|
||||
click_to_upload: 'Cliquez pour télécharger',
|
||||
or_drag_and_drop_files: 'ou faites glisser-déposer des fichiers',
|
||||
uploading: 'Téléchargement en cours',
|
||||
processing_: 'Traitement en cours...',
|
||||
add_pdf_documents_or_images: 'Ajoutez des documents PDF ou des images',
|
||||
add_documents_or_images: 'Ajoutez des documents ou des images',
|
||||
required: 'Requis',
|
||||
default_value: 'Valeur par défaut',
|
||||
format: 'Format',
|
||||
read_only: 'Lecture seule',
|
||||
page: 'Page',
|
||||
draw_new_area: 'Dessiner une nouvelle zone',
|
||||
copy_to_all_pages: 'Copier sur toutes les pages',
|
||||
add_option: 'Ajouter une option',
|
||||
option: 'Option',
|
||||
first_party: 'Première partie',
|
||||
second_party: 'Deuxième partie',
|
||||
third_party: 'Troisième partie',
|
||||
fourth_party: 'Quatrième partie',
|
||||
fifth_party: 'Cinquième partie',
|
||||
sixth_party: 'Sixième partie',
|
||||
seventh_party: 'Septième partie',
|
||||
eighth_party: 'Huitième partie',
|
||||
ninth_party: 'Neuvième partie',
|
||||
tenth_party: 'Dixième partie',
|
||||
eleventh_party: 'Onzième Parti',
|
||||
twelfth_party: 'Douzième Parti',
|
||||
thirteenth_party: 'Treizième Parti',
|
||||
fourteenth_party: 'Quatorzième Parti',
|
||||
fifteenth_party: 'Quinzième Parti',
|
||||
sixteenth_party: 'Seizième Parti',
|
||||
seventeenth_party: 'Dix-septième Parti',
|
||||
eighteenth_party: 'Dix-huitième Parti',
|
||||
nineteenth_party: 'Dix-Neuvième Fête',
|
||||
twentieth_party: 'Vingtième Fête',
|
||||
draw: 'Dessiner',
|
||||
add: 'Ajouter',
|
||||
or_add_field_without_drawing: 'Ou ajoutez un champ sans dessiner',
|
||||
text: 'Texte',
|
||||
signature: 'Signature',
|
||||
initials: 'Initiales',
|
||||
date: 'Date',
|
||||
number: 'Numéro',
|
||||
image: 'Image',
|
||||
file: 'Fichier',
|
||||
select: 'Choisir',
|
||||
checkbox: 'Coche',
|
||||
multiple: 'Multiple',
|
||||
radio: 'Radio',
|
||||
cells: 'Cellules',
|
||||
stamp: 'Tampon',
|
||||
payment: 'Paiement',
|
||||
phone: 'Téléphone',
|
||||
text_field: 'Champ de Texte',
|
||||
signature_field: 'Champ de Signature',
|
||||
initials_field: "Champ d'Initiales",
|
||||
date_field: 'Champ de Date',
|
||||
number_field: 'Champ de Numéro',
|
||||
image_field: "Champ d'Image",
|
||||
file_field: 'Champ de Fichier',
|
||||
select_field: 'Champ de Choix',
|
||||
checkbox_field: 'Champ de Coche',
|
||||
multiple_field: 'Champ de Choix Multiple',
|
||||
radio_field: 'Champ de Groupe Radio',
|
||||
cells_field: 'Champ de Cellules',
|
||||
stamp_field: 'Champ de Tampon',
|
||||
payment_field: 'Champ de Paiement',
|
||||
phone_field: 'Champ de Téléphone',
|
||||
draw_a_text_field_on_the_page_with_a_mouse: 'Dessinez un champ texte sur la page avec une souris',
|
||||
drag_and_drop_any_other_field_type_on_the_page: 'Glissez et déposez tout autre type de champ sur la page',
|
||||
click_on_the_field_type_above_to_start_drawing_it: 'Cliquez sur le type de champ ci-dessus pour commencer à le dessiner',
|
||||
please_draw_fields_to_prepare_the_document: 'Veuillez dessiner les champs pour préparer le document.',
|
||||
only_pdf_and_images_are_supported: 'Seuls les PDF et les images sont pris en charge.',
|
||||
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: 'Débloquez le champ de numéro de téléphone vérifié par SMS avec un plan payant. Utilisez un champ texte pour les numéros de téléphone sans vérification.',
|
||||
available_only_in_pro: 'Disponible uniquement en Pro',
|
||||
failed_to_download_files: 'Échec du téléchargement des fichiers',
|
||||
signature_is_too_small_please_redraw: 'La signature est trop petite. Veuillez la redessiner.',
|
||||
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: 'Veuillez ajouter des champs pour {submitter_name} ou retirer {submitter_name} si ce n\'est pas nécessaire.',
|
||||
draw_field: 'Dessiner le champ {field}',
|
||||
replace: 'Remplacer',
|
||||
uploading_: 'Téléchargement en cours...',
|
||||
add_document: 'Télécharger',
|
||||
any: 'Tout',
|
||||
drawn: 'Dessiné',
|
||||
typed: 'Tapé',
|
||||
none: 'Aucun',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
email: 'E-mail',
|
||||
url: 'URL',
|
||||
zip: 'ZIP',
|
||||
custom: 'Personnalisé',
|
||||
numbers_only: 'Chiffres uniquement',
|
||||
letters_only: 'Lettres uniquement',
|
||||
regexp_validation: 'Validation par expression régulière',
|
||||
enter_pdf_password: 'Entrez le mot de passe PDF',
|
||||
wrong_password: 'Mot de passe incorrect',
|
||||
currency: 'Devise',
|
||||
save_and_preview: 'Enregistrer et prévisualiser',
|
||||
preferences: 'Préférences',
|
||||
available_in_pro: 'Disponible en version Pro',
|
||||
some_fields_are_missing_in_the_formula: 'Certains champs manquent dans la formule.',
|
||||
learn_more: 'En savoir plus'
|
||||
}
|
||||
|
||||
const de = {
|
||||
wiederkehrend: 'Wiederkehrend',
|
||||
einmalig: 'Einmalig',
|
||||
editable: 'Bearbeitbar',
|
||||
search_field: 'Suchfeld',
|
||||
field_not_found: 'Feld nicht gefunden',
|
||||
clear: 'Löschen',
|
||||
type_value: 'Wert eingeben',
|
||||
add_all_required_fields_to_continue: 'Fügen Sie alle erforderlichen Felder hinzu, um fortzufahren',
|
||||
uploaded_pdf_contains_form_fields_keep_or_remove_them: 'Das hochgeladene PDF enthält Formularfelder. Behalten oder entfernen?',
|
||||
keep: 'Behalten',
|
||||
align: 'Ausrichten',
|
||||
left: 'Links',
|
||||
heading: 'Überschrift',
|
||||
validation: 'Validierung',
|
||||
add_blank_page: 'Leere Seite hinzufügen',
|
||||
right: 'Rechts',
|
||||
add_condition: 'Bedingung hinzufügen',
|
||||
center: 'Zentriert',
|
||||
with_logo: 'Mit Logo',
|
||||
description: 'Beschreibung',
|
||||
display_title: 'Anzeigetitel',
|
||||
unchecked: 'Nicht überprüft',
|
||||
price: 'Preis',
|
||||
equal: 'Gleich',
|
||||
not_equal: 'Ungleich',
|
||||
contains: 'Enthält',
|
||||
does_not_contain: 'Enthält nicht',
|
||||
not_empty: 'Nicht leer',
|
||||
empty: 'Leer',
|
||||
signing_date: 'Signaturdatum',
|
||||
select_field_: 'Feld auswählen...',
|
||||
select_value_: 'Wert auswählen...',
|
||||
remove_condition: 'Bedingung entfernen',
|
||||
condition: 'Bedingung',
|
||||
formula: 'Formel',
|
||||
edit: 'Bearbeiten',
|
||||
settings: 'Einstellungen',
|
||||
up: 'Nach oben',
|
||||
down: 'Nach unten',
|
||||
set_signing_date: 'Unterschriftdatum festlegen',
|
||||
are_you_sure_: 'Bist du sicher?',
|
||||
sign_yourself: 'Unterschreiben Sie selbst',
|
||||
checked: 'Ausgewählt',
|
||||
send: 'Senden',
|
||||
remove: 'Entfernen',
|
||||
save: 'Speichern',
|
||||
cancel: 'Abbrechen',
|
||||
draw_field_on_the_document: 'Ein Feld auf dem Dokument zeichnen',
|
||||
click_to_upload: 'Klicken Sie zum Hochladen',
|
||||
or_drag_and_drop_files: 'oder Dateien hierher ziehen',
|
||||
uploading: 'Hochladen',
|
||||
processing_: 'Verarbeitung...',
|
||||
add_pdf_documents_or_images: 'PDF-Dokumente oder Bilder hinzufügen',
|
||||
add_documents_or_images: 'Dokumente oder Bilder hinzufügen',
|
||||
required: 'Erforderlich',
|
||||
default_value: 'Standardwert',
|
||||
format: 'Format',
|
||||
read_only: 'Nur lesen',
|
||||
page: 'Seite',
|
||||
draw_new_area: 'Neuen Bereich zeichnen',
|
||||
copy_to_all_pages: 'Auf alle Seiten kopieren',
|
||||
add_option: 'Option hinzufügen',
|
||||
option: 'Option',
|
||||
first_party: 'Erste Partei',
|
||||
second_party: 'Zweite Partei',
|
||||
third_party: 'Dritte Partei',
|
||||
fourth_party: 'Vierte Partei',
|
||||
fifth_party: 'Fünfte Partei',
|
||||
sixth_party: 'Sechste Partei',
|
||||
seventh_party: 'Siebte Partei',
|
||||
eighth_party: 'Achte Partei',
|
||||
ninth_party: 'Neunte Partei',
|
||||
tenth_party: 'Zehnte Partei',
|
||||
eleventh_party: 'Elfte Partei',
|
||||
twelfth_party: 'Zwölfte Partei',
|
||||
thirteenth_party: 'Dreizehnte Partei',
|
||||
fourteenth_party: 'Vierzehnte Partei',
|
||||
fifteenth_party: 'Fünfzehnte Partei',
|
||||
sixteenth_party: 'Sechzehnte Partei',
|
||||
seventeenth_party: 'Siebzehnte Partei',
|
||||
eighteenth_party: 'Achtzehnte Partei',
|
||||
nineteenth_party: 'Neunzehnte Party',
|
||||
twentieth_party: 'Zwanzigste Party',
|
||||
draw: 'Zeichnen',
|
||||
add: 'Hinzufügen',
|
||||
or_add_field_without_drawing: 'Oder Feld ohne Zeichnung hinzufügen',
|
||||
text: 'Text',
|
||||
signature: 'Unterschrift',
|
||||
initials: 'Initialen',
|
||||
date: 'Datum',
|
||||
number: 'Nummer',
|
||||
image: 'Bild',
|
||||
file: 'Datei',
|
||||
select: 'Auswählen',
|
||||
checkbox: 'Checkbox',
|
||||
multiple: 'Mehrere',
|
||||
radio: 'Radio',
|
||||
cells: 'Zellen',
|
||||
stamp: 'Stempel',
|
||||
payment: 'Zahlung',
|
||||
phone: 'Telefon',
|
||||
text_field: 'Textfeld',
|
||||
signature_field: 'Unterschriftsfeld',
|
||||
initials_field: 'Initialenfeld',
|
||||
date_field: 'Datumsfeld',
|
||||
number_field: 'Zahlenfeld',
|
||||
image_field: 'Bildfeld',
|
||||
file_field: 'Dateifeld',
|
||||
select_field: 'Auswahlfeld',
|
||||
checkbox_field: 'Checkbox-Feld',
|
||||
multiple_field: 'Mehrfach-Auswahlfeld',
|
||||
radio_field: 'Radio-Gruppenfeld',
|
||||
cells_field: 'Zellenfeld',
|
||||
stamp_field: 'Stempelfeld',
|
||||
payment_field: 'Zahlungsfeld',
|
||||
phone_field: 'Telefonfeld',
|
||||
draw_a_text_field_on_the_page_with_a_mouse: 'Textfeld auf der Seite mit der Maus zeichnen',
|
||||
drag_and_drop_any_other_field_type_on_the_page: 'Ziehe und lasse einen anderen Feldtyp auf die Seite fallen',
|
||||
click_on_the_field_type_above_to_start_drawing_it: 'Klicke auf den Feldtyp oben, um mit dem Zeichnen zu beginnen',
|
||||
please_draw_fields_to_prepare_the_document: 'Bitte zeichne Felder, um das Dokument vorzubereiten.',
|
||||
only_pdf_and_images_are_supported: 'Nur PDFs und Bilder werden unterstützt.',
|
||||
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: 'Schalte das SMS-verifizierte Telefonnummernfeld mit einem kostenpflichtigen Plan frei. Verwende das Textfeld für Telefonnummern ohne Verifizierung.',
|
||||
available_only_in_pro: 'Nur in Pro verfügbar',
|
||||
failed_to_download_files: 'Fehler beim Herunterladen der Dateien',
|
||||
signature_is_too_small_please_redraw: 'Die Unterschrift ist zu klein. Bitte erneut zeichnen.',
|
||||
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: 'Bitte füge Felder für {submitter_name} hinzu oder entferne {submitter_name}, falls nicht erforderlich.',
|
||||
draw_field: 'Feld {field} zeichnen',
|
||||
replace: 'Ersetzen',
|
||||
uploading_: 'Hochladen...',
|
||||
add_document: 'Hochladen',
|
||||
any: 'Jede',
|
||||
drawn: 'Gezeichnet',
|
||||
typed: 'Getippt',
|
||||
none: 'Keine',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
email: 'E-Mail',
|
||||
url: 'URL',
|
||||
zip: 'ZIP',
|
||||
custom: 'Benutzerdefiniert',
|
||||
numbers_only: 'Nur Zahlen',
|
||||
letters_only: 'Nur Buchstaben',
|
||||
regexp_validation: 'Regulärer Ausdruck Überprüfung',
|
||||
enter_pdf_password: 'Geben Sie das PDF-Passwort ein',
|
||||
wrong_password: 'Falsches Passwort',
|
||||
currency: 'Währung',
|
||||
save_and_preview: 'Speichern und Vorschau',
|
||||
preferences: 'Einstellungen',
|
||||
available_in_pro: 'In Pro verfügbar',
|
||||
some_fields_are_missing_in_the_formula: 'Einige Felder fehlen in der Formel.',
|
||||
learn_more: 'Erfahren Sie mehr'
|
||||
}
|
||||
|
||||
export { en, es, it, pt, fr, de }
|
||||
@@ -3,7 +3,7 @@
|
||||
<div v-if="selectedSheetIndex === null && spreadsheet">
|
||||
<form @submit.prevent="[selectedSheetIndex = $refs.selectWorksheet.value, buildDefaultMappings()]">
|
||||
<label class="label">
|
||||
Select Worksheet
|
||||
{{ t('select_worksheet') }}
|
||||
</label>
|
||||
<select
|
||||
ref="selectWorksheet"
|
||||
@@ -18,7 +18,7 @@
|
||||
</option>
|
||||
</select>
|
||||
<button class="base-button mt-4 w-full">
|
||||
Open
|
||||
{{ t('open') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -36,10 +36,10 @@
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="relative w-full py-2 px-2 text-sm">
|
||||
Recipient field
|
||||
{{ t('recipient_field') }}
|
||||
</div>
|
||||
<div class="relative w-full py-2 pl-4 text-sm">
|
||||
Spreadsheet column
|
||||
{{ t('spreadsheet_column') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -50,6 +50,7 @@
|
||||
<div class="flex">
|
||||
<select
|
||||
class="base-select !select-sm !h-10"
|
||||
:class="{ '!text-gray-300': !mapping.field_name }"
|
||||
required
|
||||
@change="mapping.field_name = $event.target.value"
|
||||
>
|
||||
@@ -58,13 +59,14 @@
|
||||
value=""
|
||||
:selected="!mapping.field_name"
|
||||
>
|
||||
Select Field
|
||||
{{ t('select_field') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="(field, index) in selectFieldsForSubmitter(submitter)"
|
||||
:key="index"
|
||||
:value="field.name"
|
||||
:selected="mapping.field_name === field.name"
|
||||
class="text-base-content"
|
||||
>
|
||||
{{ field.name }}
|
||||
</option>
|
||||
@@ -75,6 +77,7 @@
|
||||
<div class="w-full relative">
|
||||
<select
|
||||
class="base-select !select-sm !h-10"
|
||||
:class="{ '!text-gray-300': !mapping.column_index }"
|
||||
required
|
||||
@change="mapping.column_index = parseInt($event.target.value)"
|
||||
>
|
||||
@@ -83,7 +86,7 @@
|
||||
value=""
|
||||
:selected="mapping.column_index == null"
|
||||
>
|
||||
Select Column
|
||||
{{ t('select_column') }}
|
||||
</option>
|
||||
<template
|
||||
v-for="(column, index) in columns"
|
||||
@@ -93,6 +96,7 @@
|
||||
v-if="column"
|
||||
:value="index"
|
||||
:selected="index === mapping.column_index"
|
||||
class="text-base-content"
|
||||
>
|
||||
{{ column }}
|
||||
</option>
|
||||
@@ -118,8 +122,8 @@
|
||||
</div>
|
||||
<div class="flex items-center pl-1">
|
||||
<span
|
||||
class="tooltip tooltip-top"
|
||||
data-tip="<%= t('remove') %>"
|
||||
class="tooltip tooltip-left"
|
||||
:data-tip="t('remove')"
|
||||
>
|
||||
<button
|
||||
:disabled="mappings.filter((m) => m.submitter_uuid === submitter.uuid).length < 2"
|
||||
@@ -138,7 +142,7 @@
|
||||
@click.prevent="addMapping(submitter)"
|
||||
>
|
||||
<IconPlus class="w-4 h-4" />
|
||||
New Field Mapping
|
||||
{{ t('new_field_mapping') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,7 +156,7 @@
|
||||
<div
|
||||
class="px-3 border-y py-2 border-base-300 text-center w-full text-sm font-semibold"
|
||||
>
|
||||
Total entries: {{ submissionsData.length }}
|
||||
{{ t('total_entries') }}: {{ submissionsData.length }}
|
||||
<template v-if="multitenant && submissionsData.length >= 1000">
|
||||
/ 1000
|
||||
</template>
|
||||
@@ -187,10 +191,10 @@
|
||||
<div
|
||||
class="font-medium text-lg mb-1"
|
||||
>
|
||||
Upload CSV or XLSX Spreadsheet
|
||||
{{ t('upload_csv_or_xlsx_spreadsheet') }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span class="font-medium">Click to Upload</span> or drag and drop files.
|
||||
<span class="font-medium">{{ t('click_to_upload') }}</span> {{ t('or_drag_and_drop_files') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -210,11 +214,11 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="text-center mt-2">
|
||||
Or <a
|
||||
{{ t('or') }} <a
|
||||
:download="`${template.name}.csv`"
|
||||
:href="`data:text/csv;base64,${csvBase64}`"
|
||||
class="link font-medium"
|
||||
>download</a> a spreadsheet to fill and import
|
||||
>{{ t('download') }}</a> {{ t('a_sample_spreadsheet_to_fill_and_import') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -248,6 +252,11 @@ export default {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
i18n: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@@ -354,6 +363,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
t (key) {
|
||||
return this.i18n[key] || key
|
||||
},
|
||||
onDropFiles (e) {
|
||||
this.uploadFile(e.dataTransfer.files[0])
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
@click="closeDropdown"
|
||||
>
|
||||
<div
|
||||
v-if="!('price_id' in field.preferences)"
|
||||
class="py-1.5 px-1 relative"
|
||||
@click.stop
|
||||
>
|
||||
@@ -58,6 +59,13 @@
|
||||
class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0"
|
||||
@blur="save"
|
||||
>
|
||||
<input
|
||||
v-else-if="'price_id' in field.preferences"
|
||||
v-model="field.preferences.price_id"
|
||||
placeholder="Price ID: price_XXXXX"
|
||||
class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0"
|
||||
@blur="save"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
v-model="field.preferences.price"
|
||||
@@ -72,8 +80,25 @@
|
||||
class="absolute -top-1 left-2.5 px-1 h-4"
|
||||
style="font-size: 8px"
|
||||
>
|
||||
Price
|
||||
{{ t('price') }}
|
||||
</label>
|
||||
<div class="flex items-center justify-center">
|
||||
<a
|
||||
href="#"
|
||||
class="hover:underline"
|
||||
style="font-size: 11px"
|
||||
:class="{'underline': !('price_id' in field.preferences)}"
|
||||
@click="delete field.preferences.price_id"
|
||||
>{{ t('one_off') }}</a>
|
||||
<span class="h-2.5 border-l border-base-content mx-1" />
|
||||
<a
|
||||
href="#"
|
||||
class="hover:underline"
|
||||
style="font-size: 11px"
|
||||
:class="{'underline': ('price_id' in field.preferences)}"
|
||||
@click="field.preferences.price_id ??= ''"
|
||||
>{{ t('recurrent') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isConnected || isOauthSuccess"
|
||||
@@ -158,7 +183,10 @@
|
||||
data-turbo="false"
|
||||
>{{ t('learn_more') }}</a>
|
||||
</div>
|
||||
<li class="mb-1">
|
||||
<li
|
||||
v-if="!('price_id' in field.preferences)"
|
||||
class="mb-1"
|
||||
>
|
||||
<label
|
||||
class="label-text cursor-pointer text-center w-full flex items-center"
|
||||
@click="$emit('click-formula')"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class GeneratePreviewImagesJob
|
||||
include Sidekiq::Job
|
||||
|
||||
sidekiq_options queue: :images
|
||||
|
||||
def perform(params = {})
|
||||
attachment = ActiveStorage::Attachment.find(params['attachment_id'])
|
||||
|
||||
max_page = [attachment.metadata['pdf']['number_of_pages'].to_i - 1,
|
||||
Templates::ProcessDocument::MAX_NUMBER_OF_PAGES_PROCESSED].min
|
||||
|
||||
Templates::ProcessDocument.generate_document_preview_images(attachment, attachment.download, (1..max_page),
|
||||
concurrency: 1)
|
||||
end
|
||||
end
|
||||
@@ -6,6 +6,8 @@ class ProcessSubmitterCompletionJob
|
||||
def perform(params = {})
|
||||
submitter = Submitter.find(params['submitter_id'])
|
||||
|
||||
create_completed_submitter!(submitter)
|
||||
|
||||
is_all_completed = !submitter.submission.submitters.exists?(completed_at: nil)
|
||||
|
||||
if !is_all_completed && submitter.submission.submitters_order_preserved?
|
||||
@@ -24,9 +26,42 @@ class ProcessSubmitterCompletionJob
|
||||
enqueue_completed_emails(submitter)
|
||||
end
|
||||
|
||||
create_completed_documents!(submitter)
|
||||
|
||||
enqueue_completed_webhooks(submitter, is_all_completed:)
|
||||
end
|
||||
|
||||
def create_completed_submitter!(submitter)
|
||||
completed_submitter = CompletedSubmitter.find_or_initialize_by(submitter_id: submitter.id)
|
||||
|
||||
return completed_submitter if completed_submitter.persisted?
|
||||
|
||||
submission = submitter.submission
|
||||
|
||||
completed_submitter.assign_attributes(
|
||||
submission_id: submitter.submission_id,
|
||||
account_id: submission.account_id,
|
||||
template_id: submission.template_id,
|
||||
source: submission.source,
|
||||
sms_count: submitter.submission_events.where(event_type: %w[send_sms send_2fa_sms]).count,
|
||||
completed_at: submitter.completed_at
|
||||
)
|
||||
|
||||
completed_submitter.save!
|
||||
|
||||
completed_submitter
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
retry
|
||||
end
|
||||
|
||||
def create_completed_documents!(submitter)
|
||||
submitter.documents.filter_map do |attachment|
|
||||
next if attachment.metadata['sha256'].blank?
|
||||
|
||||
CompletedDocument.find_or_create_by!(sha256: attachment.metadata['sha256'], submitter_id: submitter.id)
|
||||
end
|
||||
end
|
||||
|
||||
def enqueue_completed_webhooks(submitter, is_all_completed: false)
|
||||
webhook_config = Accounts.load_webhook_config(submitter.account)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: 'DocuSeal <info@docuseal.co>'
|
||||
default from: 'DocuSeal <info@docuseal.com>'
|
||||
layout 'mailer'
|
||||
|
||||
register_interceptor ActionMailerConfigsInterceptor
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
class SettingsMailer < ApplicationMailer
|
||||
def smtp_successful_setup(email)
|
||||
mail(to: email, subject: 'SMTP has been configured')
|
||||
mail(to: email, from: email, subject: 'SMTP has been configured')
|
||||
end
|
||||
end
|
||||
@@ -23,21 +23,23 @@ class SubmitterMailer < ApplicationMailer
|
||||
|
||||
reply_to = build_submitter_reply_to(@submitter)
|
||||
|
||||
subject =
|
||||
if @email_config || @subject
|
||||
ReplaceEmailVariables.call(@subject || @email_config.value['subject'], submitter:)
|
||||
elsif @submitter.with_signature_fields?
|
||||
I18n.t(:you_are_invited_to_sign_a_document)
|
||||
else
|
||||
I18n.t(:you_are_invited_to_submit_a_form)
|
||||
end
|
||||
I18n.with_locale(@current_account.locale) do
|
||||
subject =
|
||||
if @email_config || @subject
|
||||
ReplaceEmailVariables.call(@subject || @email_config.value['subject'], submitter:)
|
||||
elsif @submitter.with_signature_fields?
|
||||
I18n.t(:you_are_invited_to_sign_a_document)
|
||||
else
|
||||
I18n.t(:you_are_invited_to_submit_a_form)
|
||||
end
|
||||
|
||||
mail(
|
||||
to: @submitter.friendly_name,
|
||||
from: from_address_for_submitter(submitter),
|
||||
subject:,
|
||||
reply_to:
|
||||
)
|
||||
mail(
|
||||
to: @submitter.friendly_name,
|
||||
from: from_address_for_submitter(submitter),
|
||||
subject:,
|
||||
reply_to:
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def completed_email(submitter, user, to: nil)
|
||||
@@ -66,13 +68,15 @@ class SubmitterMailer < ApplicationMailer
|
||||
|
||||
assign_message_metadata('submitter_completed', @submitter)
|
||||
|
||||
subject =
|
||||
ReplaceEmailVariables.call(@subject.presence || I18n.t(:template_name_has_been_completed_by_submitters),
|
||||
submitter:)
|
||||
I18n.with_locale(@current_account.locale) do
|
||||
subject =
|
||||
ReplaceEmailVariables.call(@subject.presence || I18n.t(:template_name_has_been_completed_by_submitters),
|
||||
submitter:)
|
||||
|
||||
mail(from: from_address_for_submitter(submitter),
|
||||
to: to || (user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name),
|
||||
subject:)
|
||||
mail(from: from_address_for_submitter(submitter),
|
||||
to: to || normalize_user_email(user),
|
||||
subject:)
|
||||
end
|
||||
end
|
||||
|
||||
def declined_email(submitter, user)
|
||||
@@ -83,7 +87,7 @@ class SubmitterMailer < ApplicationMailer
|
||||
|
||||
assign_message_metadata('submitter_declined', @submitter)
|
||||
|
||||
I18n.with_locale(submitter.account.locale) do
|
||||
I18n.with_locale(@current_account.locale) do
|
||||
mail(from: from_address_for_submitter(submitter),
|
||||
to: user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name,
|
||||
reply_to: @submitter.friendly_name,
|
||||
@@ -116,17 +120,19 @@ class SubmitterMailer < ApplicationMailer
|
||||
assign_message_metadata('submitter_documents_copy', @submitter)
|
||||
reply_to = build_submitter_reply_to(submitter)
|
||||
|
||||
subject =
|
||||
if @subject.present?
|
||||
ReplaceEmailVariables.call(@subject, submitter:)
|
||||
else
|
||||
I18n.t(:your_document_copy)
|
||||
end
|
||||
I18n.with_locale(@current_account.locale) do
|
||||
subject =
|
||||
if @subject.present?
|
||||
ReplaceEmailVariables.call(@subject, submitter:)
|
||||
else
|
||||
I18n.t(:your_document_copy)
|
||||
end
|
||||
|
||||
mail(from: from_address_for_submitter(submitter),
|
||||
to: to || @submitter.friendly_name,
|
||||
reply_to:,
|
||||
subject:)
|
||||
mail(from: from_address_for_submitter(submitter),
|
||||
to: to || @submitter.friendly_name,
|
||||
reply_to:,
|
||||
subject:)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
@@ -171,6 +177,10 @@ class SubmitterMailer < ApplicationMailer
|
||||
documents
|
||||
end
|
||||
|
||||
def normalize_user_email(user)
|
||||
user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name
|
||||
end
|
||||
|
||||
def add_attachments_with_size_limit(storage_attachments, current_size)
|
||||
total_size = current_size
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ class UserMailer < ApplicationMailer
|
||||
|
||||
assign_message_metadata('user_invitation', @user)
|
||||
|
||||
mail(to: @user.friendly_name,
|
||||
subject: "You are invited to #{Docuseal.product_name}")
|
||||
I18n.with_locale(@current_account.locale) do
|
||||
mail(to: @user.friendly_name,
|
||||
subject: I18n.t('you_are_invited_to_product_name', product_name: Docuseal.product_name))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -28,6 +28,7 @@ class AccountConfig < ApplicationRecord
|
||||
FORCE_MFA = 'force_mfa'
|
||||
ALLOW_TYPED_SIGNATURE = 'allow_typed_signature'
|
||||
ALLOW_TO_RESUBMIT = 'allow_to_resubmit'
|
||||
ALLOW_TO_DECLINE_KEY = 'allow_to_decline'
|
||||
SUBMITTER_REMINDERS = 'submitter_reminders'
|
||||
FORM_COMPLETED_BUTTON_KEY = 'form_completed_button'
|
||||
FORM_COMPLETED_MESSAGE_KEY = 'form_completed_message'
|
||||
@@ -39,7 +40,9 @@ class AccountConfig < ApplicationRecord
|
||||
FORCE_SSO_AUTH_KEY = 'force_sso_auth'
|
||||
FLATTEN_RESULT_PDF_KEY = 'flatten_result_pdf'
|
||||
WITH_SIGNATURE_ID = 'with_signature_id'
|
||||
WITH_AUDIT_VALUES_KEY = 'with_audit_values'
|
||||
REQUIRE_SIGNING_REASON_KEY = 'require_signing_reason'
|
||||
REUSE_SIGNATURE_KEY = 'reuse_signature'
|
||||
COMBINE_PDF_RESULT_KEY = 'combine_pdf_result_key'
|
||||
|
||||
DEFAULT_VALUES = {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: completed_documents
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# sha256 :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# submitter_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_completed_documents_on_sha256 (sha256)
|
||||
# index_completed_documents_on_submitter_id (submitter_id)
|
||||
#
|
||||
class CompletedDocument < ApplicationRecord
|
||||
belongs_to :submitter, optional: true
|
||||
|
||||
has_one :completed_submitter, primary_key: :submitter_id, inverse_of: :completed_documents, dependent: :destroy
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: completed_submitters
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# completed_at :datetime not null
|
||||
# sms_count :integer not null
|
||||
# source :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
# submission_id :bigint not null
|
||||
# submitter_id :bigint not null
|
||||
# template_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_completed_submitters_on_account_id (account_id)
|
||||
# index_completed_submitters_on_submitter_id (submitter_id) UNIQUE
|
||||
#
|
||||
class CompletedSubmitter < ApplicationRecord
|
||||
belongs_to :submitter
|
||||
belongs_to :submission
|
||||
belongs_to :account
|
||||
belongs_to :template
|
||||
|
||||
has_many :completed_documents, dependent: :destroy,
|
||||
primary_key: :submitter_id,
|
||||
foreign_key: :submitter_id,
|
||||
inverse_of: :submitter
|
||||
end
|
||||
@@ -55,6 +55,8 @@ class Submission < ApplicationRecord
|
||||
has_one_attached :audit_trail
|
||||
has_one_attached :combined_document
|
||||
|
||||
has_many_attached :preview_documents
|
||||
|
||||
has_many :template_schema_documents,
|
||||
->(e) { where(uuid: (e.template_schema.presence || e.template.schema).pluck('attachment_uuid')) },
|
||||
through: :template, source: :documents_attachments
|
||||
|
||||
@@ -56,6 +56,8 @@ class Submitter < ApplicationRecord
|
||||
|
||||
has_many :document_generation_events, dependent: :destroy
|
||||
has_many :submission_events, dependent: :destroy
|
||||
has_many :start_form_submission_events, -> { where(event_type: :start_form) },
|
||||
class_name: 'SubmissionEvent', dependent: :destroy, inverse_of: :submitter
|
||||
|
||||
scope :completed, -> { where.not(completed_at: nil) }
|
||||
|
||||
@@ -86,11 +88,11 @@ class Submitter < ApplicationRecord
|
||||
end
|
||||
|
||||
def first_name
|
||||
name.split(/\s+/, 2).first
|
||||
name&.split(/\s+/, 2)&.first
|
||||
end
|
||||
|
||||
def last_name
|
||||
name.split(/\s+/, 2).last
|
||||
name&.split(/\s+/, 2)&.last
|
||||
end
|
||||
|
||||
def status_event_at
|
||||
|
||||
@@ -46,7 +46,7 @@ class Template < ApplicationRecord
|
||||
attribute :preferences, :string, default: -> { {} }
|
||||
attribute :fields, :string, default: -> { [] }
|
||||
attribute :schema, :string, default: -> { [] }
|
||||
attribute :submitters, :string, default: -> { [{ name: DEFAULT_SUBMITTER_NAME, uuid: SecureRandom.uuid }] }
|
||||
attribute :submitters, :string, default: -> { [{ name: I18n.t(:first_party), uuid: SecureRandom.uuid }] }
|
||||
attribute :slug, :string, default: -> { SecureRandom.base58(14) }
|
||||
attribute :source, :string, default: 'native'
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ class User < ApplicationRecord
|
||||
|
||||
EMAIL_REGEXP = /[^@;,<>\s]+@[^@;,<>\s]+/
|
||||
|
||||
FULL_EMAIL_REGEXP =
|
||||
/\A[a-z0-9][\.']?(?:(?:[a-z0-9_-]+[\.\+'])*[a-z0-9_-]+)*@(?:[a-z0-9]+[\.-])*[a-z0-9]+\.[a-z]{2,}\z/i
|
||||
|
||||
has_one_attached :signature
|
||||
has_one_attached :initials
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= ff.label :locale, t('time_format'), class: 'label' %>
|
||||
<%= ff.select :locale, options_for_select(controller.class::LOCALE_OPTIONS.invert, current_account.locale), {}, class: 'base-select' %>
|
||||
<%= ff.label :locale, t('language'), class: 'label' %>
|
||||
<%= ff.select :locale, options_for_select(AccountsController::LOCALE_OPTIONS.invert, current_account.locale), {}, class: 'base-select' %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -103,6 +103,18 @@
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::ALLOW_TO_DECLINE_KEY) %>
|
||||
<% if can?(:manage, account_config) %>
|
||||
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
||||
<%= f.hidden_field :key %>
|
||||
<div class="flex items-center justify-between py-2.5">
|
||||
<span>
|
||||
<%= t('allow_to_decline_documents') %>
|
||||
</span>
|
||||
<%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::FORM_PREFILL_SIGNATURE_KEY) %>
|
||||
<% if can?(:manage, account_config) %>
|
||||
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0 md:space-x-10">
|
||||
<%= render 'shared/settings_nav' %>
|
||||
<div class="flex-grow">
|
||||
<div class="flex justify-between">
|
||||
<h1 class="text-4xl font-bold mb-4">API</h1>
|
||||
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:items-end mb-4">
|
||||
<h1 class="text-4xl font-bold">API</h1>
|
||||
<%= render 'shared/test_mode_toggle' %>
|
||||
</div>
|
||||
<div class="card bg-base-200">
|
||||
@@ -10,8 +10,11 @@
|
||||
<label for="api_key" class="text-sm font-semibold">X-Auth-Token</label>
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex w-full space-x-4">
|
||||
<input id="api_key" type="text" value="<%= current_user.access_token.token %>" class="input font-mono input-bordered w-full" autocomplete="off" readonly>
|
||||
<%= render 'shared/clipboard_copy', icon: 'copy', text: current_user.access_token.token, class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
||||
<% token = current_user.access_token.token %>
|
||||
<masked-input class="block w-full" data-token="<%= token %>">
|
||||
<input id="api_key" type="text" value="<%= token.sub(token[5..], '*' * token[5..].size) %>" class="input font-mono input-bordered w-full" autocomplete="off" readonly>
|
||||
</masked-input>
|
||||
<%= render 'shared/clipboard_copy', icon: 'copy', text: token, class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
||||
</div>
|
||||
<%= button_to button_title(title: t('rotate'), disabled_with: t('rotate'), icon: svg_icon('reload', class: 'w-6 h-6')), settings_api_index_path, class: 'white-button w-full', data: { turbo_confirm: t('remove_existing_api_token_and_generated_a_new_one_are_you_sure_') } %>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="text-center">
|
||||
<div class="join">
|
||||
<a href="https://docuseal.co<%= request.fullpath.gsub('docuseal.eu', 'docuseal.co') %>" class="btn bg-base-200 join-item w-32 <%= 'bg-base-300' if request.host == 'docuseal.co' %>">
|
||||
<a href="https://docuseal.com<%= request.fullpath.gsub('docuseal.eu', 'docuseal.com') %>" class="btn bg-base-200 join-item w-32 <%= 'bg-base-300' if request.host == 'docuseal.com' %>">
|
||||
<%= svg_icon 'world', class: 'w-5 h-5' %>
|
||||
Global
|
||||
</a>
|
||||
<a href="https://docuseal.eu<%= request.fullpath.gsub('docuseal.co', 'docuseal.eu') %>" class="btn bg-base-200 join-item w-32 <%= 'bg-base-300' if request.host == 'docuseal.eu' %>">
|
||||
<a href="https://docuseal.eu<%= request.fullpath.gsub('docuseal.com', 'docuseal.eu') %>" class="btn bg-base-200 join-item w-32 <%= 'bg-base-300' if request.host == 'docuseal.eu' %>">
|
||||
<%= svg_icon 'eu_flag', class: 'w-5 h-5' %>
|
||||
Europe
|
||||
</a>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="form-control">
|
||||
<%= ff.label :security_label, 'SMTP Security', class: 'label' %>
|
||||
<div class="flex items-center space-x-6">
|
||||
<% [%w[Auto none], %w[SSL ssl], %w[TLS tls]].each do |(label, val)| %>
|
||||
<% [%w[Auto none], %w[SSL ssl], %w[TLS tls], %w[Noverify noverify]].each do |(label, val)| %>
|
||||
<%= 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' %>
|
||||
<%= label %>
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= ff.label :from_email, t('send_from_email'), class: 'label' %>
|
||||
<%= ff.email_field :from_email, value: value['from_email'], required: true, class: 'base-input' %>
|
||||
<%= ff.email_field :from_email, value: value['from_email'], required: !Docuseal.multitenant?, class: 'base-input' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="form-control pt-2">
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<tr scope="row" class="group">
|
||||
<td class="flex items-center space-x-1">
|
||||
<%= svg_icon('discount_check_filled', class: 'w-6 h-6 text-green-500') %>
|
||||
<span class="flex items-center">
|
||||
<%= t('docuseal_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') %>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<%= "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" class="btn btn-neutral btn-sm text-white">
|
||||
<%= t('unlock_with_docuseal_pro') %>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="tooltip" data-tip="<%= t('unlock_with_docuseal_pro') %>">
|
||||
<%= button_to settings_esign_path, method: :put, params: { name: Docuseal::AATL_CERT_NAME }, class: 'btn btn-outline btn-neutral btn-xs whitespace-nowrap', title: t('make_default'), disabled: true do %>
|
||||
<%= t('make_default') %>
|
||||
<% end %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= f.label :file, t('file'), class: 'label' %>
|
||||
<%= f.file_field :file, required: true %>
|
||||
<%= f.file_field :file, class: 'file-input file-input-bordered w-full', required: true %>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">
|
||||
<%= t('use_a_valid_der_p12_or_pfx_file') %>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<% end %>
|
||||
<file-dropzone data-name="verify_attachments" data-submit-on-upload="true" class="w-full">
|
||||
<label for="file" class="w-full block h-32 relative bg-base-200 hover:bg-base-200/70 rounded-md border border-base-content border-dashed">
|
||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center p-2">
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<span data-target="file-dropzone.icon">
|
||||
<%= svg_icon('cloud_upload', class: 'w-10 h-10') %>
|
||||
</span>
|
||||
@@ -40,7 +40,7 @@
|
||||
</file-dropzone>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="flex justify-between 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-3xl font-bold">
|
||||
<%= t('signing_certificates') %>
|
||||
</h2>
|
||||
@@ -71,6 +71,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @pkcs_list.each do |item| %>
|
||||
<% next if item['pkcs'].blank? %>
|
||||
<tr scope="row" class="group">
|
||||
<td>
|
||||
<%= item['name'] %>
|
||||
@@ -98,6 +99,9 @@
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% unless Docuseal.multitenant? %>
|
||||
<%= render 'default_signature_row' %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12.01 2.011a3.2 3.2 0 0 1 2.113 .797l.154 .145l.698 .698a1.2 1.2 0 0 0 .71 .341l.135 .008h1a3.2 3.2 0 0 1 3.195 3.018l.005 .182v1c0 .27 .092 .533 .258 .743l.09 .1l.697 .698a3.2 3.2 0 0 1 .147 4.382l-.145 .154l-.698 .698a1.2 1.2 0 0 0 -.341 .71l-.008 .135v1a3.2 3.2 0 0 1 -3.018 3.195l-.182 .005h-1a1.2 1.2 0 0 0 -.743 .258l-.1 .09l-.698 .697a3.2 3.2 0 0 1 -4.382 .147l-.154 -.145l-.698 -.698a1.2 1.2 0 0 0 -.71 -.341l-.135 -.008h-1a3.2 3.2 0 0 1 -3.195 -3.018l-.005 -.182v-1a1.2 1.2 0 0 0 -.258 -.743l-.09 -.1l-.697 -.698a3.2 3.2 0 0 1 -.147 -4.382l.145 -.154l.698 -.698a1.2 1.2 0 0 0 .341 -.71l.008 -.135v-1l.005 -.182a3.2 3.2 0 0 1 3.013 -3.013l.182 -.005h1a1.2 1.2 0 0 0 .743 -.258l.1 -.09l.698 -.697a3.2 3.2 0 0 1 2.269 -.944zm3.697 7.282a1 1 0 0 0 -1.414 0l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.32 1.497l2 2l.094 .083a1 1 0 0 0 1.32 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z" stroke-width="0" fill="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -6,7 +6,7 @@
|
||||
<span>
|
||||
<%= t('completed_documents_notification_bcc_address') %>
|
||||
</span>
|
||||
<span class="tooltip" data-tip="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') %>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -3,22 +3,22 @@
|
||||
<div class="form-control">
|
||||
<% record = Struct.new(:first_duration, :second_duration, :third_duration).new(*(f.object.value || {}).values_at('first_duration', 'second_duration', 'third_duration')) %>
|
||||
<% durations = (Docuseal.multitenant? ? AccountConfigs::REMINDER_DURATIONS.except('one_hour', 'two_hours') : AccountConfigs::REMINDER_DURATIONS).keys.map { |v| [t(v.underscore), v] } %>
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex flex-col md:flex-row gap-2">
|
||||
<div class="w-full">
|
||||
<%= f.fields_for :value, record do |ff| %>
|
||||
<%= ff.label :first_duration, t('first_reminder_in'), class: 'label' %>
|
||||
<%= ff.label :first_duration, t('first_reminder_in'), class: 'label truncate' %>
|
||||
<%= ff.select :first_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<%= f.fields_for :value, record do |ff| %>
|
||||
<%= ff.label :second_duration, t('second_reminder_in'), class: 'label' %>
|
||||
<%= ff.label :second_duration, t('second_reminder_in'), class: 'label truncate' %>
|
||||
<%= ff.select :second_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<%= f.fields_for :value, record do |ff| %>
|
||||
<%= ff.label :third_duration, t('third_reminder_in'), class: 'label' %>
|
||||
<%= ff.label :third_duration, t('third_reminder_in'), class: 'label truncate' %>
|
||||
<%= ff.select :third_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<input type="checkbox">
|
||||
<div class="collapse-title text-xl font-medium capitalize">
|
||||
<div>
|
||||
<%= t('copleted_notification_email') %>
|
||||
<%= t('completed_notification_email') %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
|
||||
@@ -56,6 +56,12 @@
|
||||
<%= ff.text_field :value, required: true, class: 'base-input' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= fields_for @account do |ff| %>
|
||||
<div class="form-control">
|
||||
<%= ff.label :locale, t('language'), class: 'label' %>
|
||||
<%= ff.select :locale, options_for_select(AccountsController::LOCALE_OPTIONS.invert, @account.locale), {}, class: 'base-select' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= f.button button_title, class: 'base-button' %>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<span class="peer-checked:hidden flex items-center space-x-2">
|
||||
<%= svg_icon(local_assigns[:icon] || 'link', class: local_assigns[:icon_class] || 'w-6 h-6 text-white') %>
|
||||
<span class="hidden md:inline">
|
||||
<%= local_assigns[:copy_title] || 'Copy' %>
|
||||
<%= local_assigns[:copy_title] || t('copy') %>
|
||||
</span>
|
||||
<% if local_assigns[:copy_title_md] %>
|
||||
<span class="inline md:hidden">
|
||||
@@ -15,7 +15,7 @@
|
||||
<span class="hidden peer-checked:flex items-center space-x-2">
|
||||
<%= svg_icon(local_assigns[:copied_icon] || 'clipboard_copy', class: local_assigns[:icon_class] || 'w-6 h-6 text-white') %>
|
||||
<span class="hidden md:inline">
|
||||
<%= local_assigns[:copied_title] || 'Copied' %>
|
||||
<%= local_assigns[:copied_title] || t('copied') %>
|
||||
</span>
|
||||
<% if local_assigns[:copied_title_md] %>
|
||||
<span class="inline md:hidden">
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
---
|
||||
</p>
|
||||
<p>
|
||||
Sent using <a href="<%= Docuseal::PRODUCT_URL %>/start"><%= Docuseal.product_name %></a> free document signing.
|
||||
<%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_URL}/start", product_name: Docuseal.product_name) %>
|
||||
</p>
|
||||
@@ -15,5 +15,5 @@
|
||||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
<label class="modal-backdrop" for="<%= uuid %>"><%= t('close') %></label>
|
||||
<label class="modal-backdrop" for="<%= uuid %>"></label>
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
<% if signed_in? %>
|
||||
<div class="space-x-4 flex items-center">
|
||||
<% if Docuseal.demo? %>
|
||||
<a href="https://docuseal.co/sign_up" class="btn btn-neutral btn-sm btn-outline inline-flex items-center justify-center" style="height: 37px">
|
||||
<a href="https://docuseal.com/sign_up" class="btn btn-neutral btn-sm btn-outline inline-flex items-center justify-center" style="height: 37px">
|
||||
<%= t('sign_up') %>
|
||||
</a>
|
||||
<span class="hidden sm:inline">
|
||||
@@ -26,7 +26,7 @@
|
||||
<label tabindex="0" class="cursor-pointer bg-base-content text-purple-300 rounded-full p-2 w-9 justify-center flex">
|
||||
<span class="text-sm align-text-top"><%= current_user.initials %></span>
|
||||
</label>
|
||||
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box w-40 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>
|
||||
<%= link_to settings_profile_index_path, class: 'flex items-center' do %>
|
||||
<%= svg_icon('adjustments', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
@@ -36,7 +36,7 @@
|
||||
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) %>
|
||||
<li>
|
||||
<%= link_to Docuseal.multitenant? ? console_redirect_index_path : Docuseal::CONSOLE_URL, data: { prefetch: false }, class: 'flex items-center' do %>
|
||||
<%= svg_icon('terminal', class: 'w-5 h-5 stroke-2') %>
|
||||
<%= svg_icon('terminal', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
<%= t('console') %>
|
||||
<% end %>
|
||||
</li>
|
||||
@@ -44,15 +44,15 @@
|
||||
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::ESIGN_CERTS_KEY, account: current_account)) %>
|
||||
<li>
|
||||
<%= link_to settings_esign_path, class: 'flex items-center' do %>
|
||||
<%= svg_icon('zoom_check', class: 'w-5 h-5 stroke-2') %>
|
||||
<span class="mr-1"><%= t('verify_pdf') %></span>
|
||||
<%= svg_icon('zoom_check', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
<span class="mr-1 whitespace-nowrap"><%= t('verify_pdf') %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<%= button_to destroy_user_session_path, method: :delete, data: { turbo: false }, class: 'flex items-center' do %>
|
||||
<%= svg_icon('logout', class: 'w-5 h-5 stroke-2 mr-2 inline') %>
|
||||
<span class="mr-1"><%= t('sign_out') %></span>
|
||||
<%= svg_icon('logout', class: 'w-5 h-5 flex-shrink-0 stroke-2 mr-2 inline') %>
|
||||
<span class="mr-1 whitespace-nowrap"><%= t('sign_out') %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="text-center px-2">
|
||||
<% if local_assigns[:with_counter] %>
|
||||
<% count = Submitter.where.not(completed_at: nil).distinct.count(:submission_id) %>
|
||||
<% count = CompletedSubmitter.distinct.count(:submission_id) %>
|
||||
<% if count > 1 %>
|
||||
<%= t('count_documents_signed_with_html', count:) %>
|
||||
<% else %>
|
||||
@@ -9,5 +9,5 @@
|
||||
<% else %>
|
||||
<%= t('powered_by') %>
|
||||
<% end %>
|
||||
<a href="<%= Docuseal::PRODUCT_URL %><%= local_assigns[:link_path] %>" class="underline"><%= Docuseal.product_name %></a> - open source documents software
|
||||
<a href="<%= Docuseal::PRODUCT_URL %><%= local_assigns[:link_path] %>" class="underline"><%= Docuseal.product_name %></a> - <%= t('open_source_documents_software') %>
|
||||
</div>
|
||||
@@ -4,15 +4,15 @@
|
||||
<% end %>
|
||||
<% if params[:q].present? %>
|
||||
<div class="relative">
|
||||
<a href="<%= url_for(params.to_unsafe_h.except(:q)) %>" title="Clear" class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-auto text-neutral text-2xl font-extralight">
|
||||
<a href="<%= url_for(params.to_unsafe_h.except(:q)) %>" title="<%= t('clear') %>" class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-auto text-neutral text-2xl font-extralight">
|
||||
×
|
||||
</a>
|
||||
</div>
|
||||
<% end %>
|
||||
<search-input data-title="<%= local_assigns[:title_selector] || 'h1' %>">
|
||||
<input id="search" name="q" value="<%= params[:q] %>" class="input input-ghost text-lg pr-10 -mr-12 w-0 md:w-64 <%= 'pl-8 input-outlined w-64' if params[:q].present? %>" placeholder="<%= local_assigns[:placeholder] %>">
|
||||
<input id="search" name="q" value="<%= params[:q] %>" class="input text-lg pr-10 -mr-12 w-0 md:w-60 <%= 'pl-8 input-outlined w-60' if params[:q].present? %>" placeholder="<%= local_assigns[:placeholder] %>">
|
||||
</search-input>
|
||||
<button type="submit" title="Search" class="btn btn-ghost btn-circle" onclick="window.search.value || document.activeElement === window.search ? null : [event.preventDefault(), window.search.focus()]">
|
||||
<button type="submit" title="<%= t('search') %>" class="btn btn-ghost btn-circle" onclick="window.search.value || document.activeElement === window.search ? null : [event.preventDefault(), window.search.focus()]">
|
||||
<span class="enabled">
|
||||
<%= svg_icon('search', class: 'w-6 h-6 stroke-2') %>
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="block w-full md:w-52 flex-none">
|
||||
<menu-active>
|
||||
<ul class="menu px-0">
|
||||
<li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium">← Back</a></li>
|
||||
<li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium">← <%= t('back') %></a></li>
|
||||
<li class="menu-title py-0 !bg-transparent">
|
||||
<span class="!bg-transparent"><%= t('settings') %></span>
|
||||
</li>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<% if signed_in? && current_user != true_user && current_account.testing? %>
|
||||
<div class="alert py-1 text-sm font-medium gap-x-2 flex whitespace-nowrap">
|
||||
<div class="hidden md:flex alert py-1 text-sm font-medium gap-x-2 whitespace-nowrap">
|
||||
<a href="<%= testing_api_settings_path %>" data-turbo-frame="modal" class="link font-semibold flex">
|
||||
<%= svg_icon('code_circle', class: 'w-5 h-5 mr-1') %>
|
||||
<span><%= t('testing_environment') %></span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<% if can?(:manage, EncryptedConfig) || (current_user != true_user && current_account.testing?) %>
|
||||
<%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'flex' } do |f| %>
|
||||
<label class="flex items-center justify-between py-2.5" for="testing_toggle">
|
||||
<label class="flex items-center justify-between" for="testing_toggle">
|
||||
<span class="mr-2 text-lg">
|
||||
<%= t('test_environment') %>
|
||||
</span>
|
||||
|
||||
@@ -20,18 +20,18 @@
|
||||
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
|
||||
<submitters-autocomplete data-field="name">
|
||||
<linked-input data-target-id="<%= "detailed_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<%= tag.input type: 'text', name: 'submission[1][submitters][][name]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: 'Name', required: index.zero?, value: (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '', dir: 'auto', id: "detailed_name_#{item['uuid']}" %>
|
||||
<%= tag.input type: 'text', name: 'submission[1][submitters][][name]', autocomplete: 'off', class: 'base-input !h-10 w-full', placeholder: t('name'), required: index.zero?, value: (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '', dir: 'auto', id: "detailed_name_#{item['uuid']}" %>
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
<div class="grid <%= 'md:grid-cols-2 gap-1' if submitters.size == 1 %>">
|
||||
<submitters-autocomplete data-field="email">
|
||||
<linked-input data-target-id="<%= "detailed_email_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<input type="email" multiple name="submission[1][submitters][][email]" autocomplete="off" class="base-input-slim mt-1.5 w-full" placeholder="Email (optional)" value="<%= item['email'].presence || ((params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.email : '') %>" id="detailed_email_<%= item['uuid'] %>">
|
||||
<input type="email" multiple name="submission[1][submitters][][email]" autocomplete="off" class="base-input !h-10 mt-1.5 w-full" placeholder="<%= "#{t('email')} (#{t('optional')})" %>" value="<%= item['email'].presence || ((params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.email : '') %>" id="detailed_email_<%= item['uuid'] %>">
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
<submitters-autocomplete data-field="phone">
|
||||
<linked-input data-target-id="<%= "detailed_phone_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<input type="tel" pattern="^\+[0-9\s\-]+$" oninvalid="this.value ? this.setCustomValidity('Use internatioanl format: +1xxx...') : ''" oninput="this.setCustomValidity('')" name="submission[1][submitters][][phone]" autocomplete="off" class="base-input-slim mt-1.5 w-full" placeholder="Phone (optional)" id="detailed_phone_<%= item['uuid'] %>">
|
||||
<input type="tel" pattern="^\+[0-9\s\-]+$" oninvalid="this.value ? this.setCustomValidity('<%= t('use_international_format_1xxx_') %>') : ''" oninput="this.setCustomValidity('')" name="submission[1][submitters][][phone]" autocomplete="off" class="base-input !h-10 mt-1.5 w-full" placeholder="<%= "#{t('phone')} (#{t('optional')})" %>" id="detailed_phone_<%= item['uuid'] %>">
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
|
||||
<submitters-autocomplete data-field="email">
|
||||
<linked-input data-target-id="<%= "email_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<%= tag.input type: 'email', multiple: true, name: 'submission[1][submitters][][email]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: t('email'), required: index.zero?, value: item['email'].presence || ((params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.email : ''), id: "email_#{item['uuid']}" %>
|
||||
<%= tag.input type: 'email', multiple: true, name: 'submission[1][submitters][][email]', autocomplete: 'off', class: 'base-input !h-10 w-full', placeholder: t('email'), required: index.zero?, value: item['email'].presence || ((params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.email : ''), id: "email_#{item['uuid']}" %>
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
</submitter-item>
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
|
||||
<submitters-autocomplete data-field="phone">
|
||||
<linked-input data-target-id="<%= "phone_phone_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('Use internatioanl format: +1xxx...') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: t('phone'), required: index.zero?, id: "phone_phone_#{item['uuid']}" %>
|
||||
<%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('#{t('use_international_format_1xxx_')}') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 w-full', placeholder: t('phone'), required: index.zero?, id: "phone_phone_#{item['uuid']}" %>
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
<% if submitters.size > 1 %>
|
||||
<submitters-autocomplete data-field="name">
|
||||
<linked-input data-target-id="<%= "phone_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input-slim mt-1.5 w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>">
|
||||
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input !h-10 mt-1.5 w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>">
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
<% end %>
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="form-control flex">
|
||||
<submitters-autocomplete data-field="name">
|
||||
<linked-input data-target-id="<%= "phone_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
|
||||
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input-slim w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>">
|
||||
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input !h-10 w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>">
|
||||
</linked-input>
|
||||
</submitters-autocomplete>
|
||||
</div>
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<% can_send_emails = Accounts.can_send_emails?(current_account) %>
|
||||
<div class="flex justify-between items-center">
|
||||
<%= f.label :send_email, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
|
||||
<%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails, checked: can_send_emails %>
|
||||
<span class="label"><%= t('send_emails') %></span>
|
||||
<%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails, checked: can_send_emails && !local_assigns.key?(:resend_email) %>
|
||||
<span class="label"><%= local_assigns[:resend_email] ? t('re_send_email') : t('send_email') %></span>
|
||||
<% end %>
|
||||
<div>
|
||||
<% if can_send_emails %>
|
||||
<%= render 'email_stats' %>
|
||||
<%= render 'submissions/email_stats' %>
|
||||
<%= content_for(:edit_button) || capture do %>
|
||||
<label>
|
||||
<%= f.check_box :is_custom_message, onchange: "[this.form.querySelector('#message_field').classList.toggle('hidden', !event.currentTarget.checked)]", checked: false, class: 'hidden peer' %>
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="form-control space-y-2">
|
||||
<div class="form-control">
|
||||
<%= f.label :subject, t('subject'), class: 'label' %>
|
||||
<%= f.text_field :subject, value: @template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
|
||||
<%= f.text_field :subject, value: local_assigns[:submitter_email_message]&.subject.presence || template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<div class="flex items-center">
|
||||
@@ -49,14 +49,16 @@
|
||||
</span>
|
||||
</div>
|
||||
<autoresize-textarea>
|
||||
<%= f.text_area :body, value: @template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
|
||||
<%= f.text_area :body, value: local_assigns[:submitter_email_message]&.body.presence || template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
|
||||
</autoresize-textarea>
|
||||
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
|
||||
<%= check_box_tag :save_message, id: uuid, class: 'base-checkbox', checked: false %>
|
||||
<span class="label"><%= t('save_as_default_template_message') %></span>
|
||||
</label>
|
||||
<% unless local_assigns.fetch(:disable_save_as_default_template_option, false) %>
|
||||
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
|
||||
<%= check_box_tag :save_message, id: uuid, class: 'base-checkbox', checked: false %>
|
||||
<span class="label"><%= t('save_as_default_template_message') %></span>
|
||||
</label>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render 'message_fields' %>
|
||||
<%= render 'submissions/message_fields' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,4 +1,6 @@
|
||||
<% if template.submitters.size > 1 %>
|
||||
<% if template.preferences['submitters_order'] == 'preserved' %>
|
||||
<%= f.hidden_field :preserve_order, value: '1' %>
|
||||
<% elsif template.submitters.size > 1 %>
|
||||
<div class="form-control">
|
||||
<%= f.label :preserve_order, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
|
||||
<%= f.check_box :preserve_order, id: uuid, class: 'base-checkbox', checked: template.submissions.last&.submitters_order.in?(['preserved', nil]) %>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<% align = field.dig('preferences', 'align') %>
|
||||
<field-value dir="auto" class="flex absolute text-[1.6vw] lg:text-base <%= 'font-serif' if field.dig('preferences', 'font') == 'Times' %> <%= align == 'right' ? 'justify-end' : (align == 'center' ? 'justify-center' : '') %>" style="width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%; <%= "font-size: #{field['preferences']['font_size']}pt" if field.dig('preferences', 'font_size') %>">
|
||||
<field-value dir="auto" class="flex absolute text-[1.6vw] lg:text-base <%= 'font-serif' if field.dig('preferences', 'font') == 'Times' %> <%= align == 'right' ? 'justify-end' : (align == 'center' ? 'justify-center' : '') %>" style="width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%; <%= "font-size: clamp(4pt, 1.6vw, #{field['preferences']['font_size']}pt); line-height: `clamp(6pt, 2.0vw, #{field['preferences']['font_size'].to_i + 3}pt)`" if field.dig('preferences', 'font_size') %>">
|
||||
<% if field['type'] == 'signature' %>
|
||||
<div class="flex flex-col justify-between h-full overflow-hidden">
|
||||
<div class="flex-grow flex overflow-hidden" style="min-height: 50%">
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<%= render 'shared/turbo_modal_large', title: params[:selfsign] ? t('add_recipients') : t('add_new_recipients') do %>
|
||||
<% options = [['via Email', 'email'], ['via Phone', 'phone'], %w[Detailed detailed], ['Upload List', 'list']].compact %>
|
||||
<% options = [[t('via_email'), 'email'], [t('via_phone'), 'phone'], [t('detailed'), 'detailed'], [t('upload_list'), 'list']].compact %>
|
||||
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center px-2 mt-4 block">
|
||||
<div class="join">
|
||||
<div class="flex justify-center">
|
||||
<% options.each_with_index do |(label, value), index| %>
|
||||
<span>
|
||||
<div>
|
||||
<%= radio_button_tag 'option', value, value == 'email', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %>
|
||||
<label for="option_<%= value %>" class="<%= '!rounded-s-full' if index.zero? %> btn btn-focus btn-sm join-item md:w-28 peer-checked:btn-active normal-case">
|
||||
<label for="option_<%= value %>" class="block bg-base-200 md:min-w-[112px] text-sm font-semibold whitespace-nowrap py-1.5 px-4 peer-checked:bg-base-300 <%= 'hidden sm:inline-block' if value == 'list' %> <%= 'rounded-l-3xl' if index.zero? %> <%= 'rounded-r-3xl sm:rounded-r-none' if value == 'detailed' %> <%= 'rounded-r-3xl' if index == options.size - 1 %>">
|
||||
<%= label %>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</toggle-visible>
|
||||
|
||||
@@ -17,43 +17,45 @@
|
||||
<% end %>
|
||||
<% if last_submitter = @submission.submitters.to_a.select(&:completed_at?).max_by(&:completed_at) %>
|
||||
<% is_all_completed = @submission.submitters.to_a.all?(&:completed_at?) %>
|
||||
<div class="join relative">
|
||||
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: is_combined_enabled }.compact) %>" class="base-button <%= '!rounded-r-none !pr-2' if is_all_completed && !is_combined_enabled %>">
|
||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||
<%= svg_icon('download', class: 'w-6 h-6') %>
|
||||
<span class="hidden md:inline"><%= t('download') %></span>
|
||||
</span>
|
||||
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
|
||||
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
|
||||
<span class="hidden md:inline"><%= t('downloading') %></span>
|
||||
</span>
|
||||
</download-button>
|
||||
<% if is_all_completed && !is_combined_enabled %>
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="0" class="base-button !rounded-l-none !pl-1 !pr-2 !border-l-neutral-500">
|
||||
<span class="text-sm align-text-top">
|
||||
<%= svg_icon('chevron_down', class: 'w-6 h-6 flex-shrink-0 stroke-2') %>
|
||||
</span>
|
||||
</label>
|
||||
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box text-right">
|
||||
<li>
|
||||
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: true }.compact) %>" class="flex items-center">
|
||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||
<%= svg_icon('download', class: 'w-6 h-6 flex-shrink-0') %>
|
||||
<span class="whitespace-nowrap"><%= t('download_combined_pdf') %></span>
|
||||
</span>
|
||||
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
|
||||
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
|
||||
<span><%= t('downloading') %></span>
|
||||
</span>
|
||||
</download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if is_all_completed || !is_combined_enabled %>
|
||||
<div class="join relative">
|
||||
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: is_combined_enabled }.compact) %>" class="base-button <%= '!rounded-r-none !pr-2' if is_all_completed && !is_combined_enabled %>">
|
||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||
<%= svg_icon('download', class: 'w-6 h-6') %>
|
||||
<span class="hidden md:inline"><%= t('download') %></span>
|
||||
</span>
|
||||
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
|
||||
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
|
||||
<span class="hidden md:inline"><%= t('downloading') %></span>
|
||||
</span>
|
||||
</download-button>
|
||||
<% if is_all_completed && !is_combined_enabled %>
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="0" class="base-button !rounded-l-none !pl-1 !pr-2 !border-l-neutral-500">
|
||||
<span class="text-sm align-text-top">
|
||||
<%= svg_icon('chevron_down', class: 'w-6 h-6 flex-shrink-0 stroke-2') %>
|
||||
</span>
|
||||
</label>
|
||||
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box text-right">
|
||||
<li>
|
||||
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: true }.compact) %>" class="flex items-center">
|
||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||
<%= svg_icon('download', class: 'w-6 h-6 flex-shrink-0') %>
|
||||
<span class="whitespace-nowrap"><%= t('download_combined_pdf') %></span>
|
||||
</span>
|
||||
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
|
||||
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
|
||||
<span><%= t('downloading') %></span>
|
||||
</span>
|
||||
</download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% elsif @submission.submitters.to_a.size == 1 && !@submission.expired? && !@submission.submitters.to_a.first.declined_at? %>
|
||||
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: @submission.submitters.to_a.first.slug), class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Share Link', copied_title: 'Copied to Clipboard' %>
|
||||
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: @submission.submitters.to_a.first.slug), class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: t('copy_share_link'), copied_title: t('copied_to_clipboard') %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,12 +109,21 @@
|
||||
<% (@submission.template_submitters || @submission.template.submitters).each_with_index do |item, index| %>
|
||||
<% submitter = @submission.submitters.find { |e| e.uuid == item['uuid'] } %>
|
||||
<div class="sticky -top-1 bg-base-100 pt-1 -mt-1">
|
||||
<div class="border border-base-300 rounded-md px-2 py-1 mb-1">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class="mx-1 w-3 h-3 rounded-full <%= colors[index] %>"></span>
|
||||
<span class="text-lg" dir="auto">
|
||||
<%= (@submission.template_submitters || @submission.template.submitters).find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %>
|
||||
</span>
|
||||
<div class="group border border-base-300 rounded-md px-2 py-1 mb-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class="mx-1 w-3 h-3 shrink-0 rounded-full <%= colors[index] %>"></span>
|
||||
<span class="text-lg" dir="auto">
|
||||
<%= (@submission.template_submitters || @submission.template.submitters).find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %>
|
||||
</span>
|
||||
</div>
|
||||
<% if signed_in? && can?(:update, submitter) && !submitter.completed_at? && !submitter.declined_at? && !@submission.archived_at? && !@submission.expired? && !submitter.start_form_submission_events.any? %>
|
||||
<span class="tooltip tooltip-left" data-tip="<%= t('edit') %>">
|
||||
<%= 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') %>
|
||||
<% end %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if submitter&.name.present? %>
|
||||
<div class="flex items-center space-x-1 mt-1">
|
||||
@@ -144,7 +155,7 @@
|
||||
<% if submitter&.declined_at? %>
|
||||
<%= t('declined_on_time', time: l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
|
||||
<% elsif submitter %>
|
||||
<%= submitter.completed_at? ? l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) : t('not_copleted_yet') %>
|
||||
<%= submitter.completed_at? ? l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) : t('not_completed_yet') %>
|
||||
<% else %>
|
||||
<%= t('not_invited_yet') %>
|
||||
<% end %>
|
||||
@@ -153,14 +164,14 @@
|
||||
<% if submitter&.declined_at? %>
|
||||
<div class="flex items-center space-x-1 mt-1">
|
||||
<span>
|
||||
<%= t('Reason') %>:
|
||||
<%= t('reason') %>:
|
||||
<%= simple_format(h(submitter.submission_events.find_by(event_type: :decline_form).data['reason'])) %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && Accounts.can_send_emails?(current_account) && !@submission.expired? && !submitter.declined_at? %>
|
||||
<div class="mt-2 mb-1">
|
||||
<%= button_to button_title(title: submitter.sent_at? ? 'Re-send Email' : 'Send Email', disabled_with: t('sending')), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %>
|
||||
<%= button_to button_title(title: submitter.sent_at? ? t('re_send_email') : t('send_email'), disabled_with: t('sending')), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if signed_in? && submitter && submitter.phone && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && !@submission.expired? && !submitter.declined_at? %>
|
||||
@@ -182,7 +193,7 @@
|
||||
<% next if value.blank? %>
|
||||
<div class="pt-2.5 border-b border-base-300">
|
||||
<div class="text-xs font-medium uppercase mb-0.5" dir="auto">
|
||||
<%= field['name'].presence || "#{field['type'].titleize} Field #{submitter_field_counters[field['type']]}" %>
|
||||
<%= field['name'].presence || "#{t("#{field['type']}_field")} #{submitter_field_counters[field['type']]}" %>
|
||||
</div>
|
||||
<div dir="auto">
|
||||
<% if field['type'].in?(%w[signature initials]) %>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<% is_show_tabs = @pagy.count >= 5 || params[:status].present? %>
|
||||
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
||||
<div class="flex justify-between mb-4 items-center">
|
||||
<div class="flex items-center">
|
||||
<div class="flex justify-between items-center w-full mb-4">
|
||||
<div class="flex items-center flex-grow min-w-0">
|
||||
<div class="mr-2">
|
||||
<%= render 'dashboard/toggle_view', selected: 'submissions' %>
|
||||
</div>
|
||||
<h1 class="text-2xl md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
|
||||
<h1 class="text-2xl truncate md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
|
||||
<%= t('submissions') %>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= render 'shared/turbo_modal', title: 'Export', close_after_submit: false do %>
|
||||
<%= render 'shared/turbo_modal', title: t('export'), close_after_submit: false do %>
|
||||
<div class="space-y-2">
|
||||
<%= button_to template_submissions_export_index_path(@template), params: { format: :xlsx }, method: :get, data: { turbo_frame: :_top } do %>
|
||||
<div class="flex items-center p-4 text-left rounded-2xl border border-neutral-300 hover:cursor-pointer hover:bg-neutral hover:text-gray-300">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], methods: %i[url filename content_type]) %>
|
||||
<% data_fields = (submitter.submission.template_fields || submitter.submission.template.fields).select { |f| f['submitter_uuid'] == submitter.uuid }.to_json %>
|
||||
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template.preferences['completed_redirect_url'] %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template.preferences['completed_message'] || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>"></submission-form>
|
||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template.preferences['completed_redirect_url'] %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template.preferences['completed_message'] || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>
|
||||
Loaded 100 of 168 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user