Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef05f030de | ||
|
|
b8c36ff09c | ||
|
|
1ba8a9a374 | ||
|
|
661e9b69fb | ||
|
|
a1aeabccff | ||
|
|
0e5e0e7772 | ||
|
|
2a74fdd095 | ||
|
|
ac426e55b2 | ||
|
|
ad2b35673a | ||
|
|
af1e8fa6f2 | ||
|
|
5800cb6c5a | ||
|
|
64d3360e82 | ||
|
|
3ae98bf03e | ||
|
|
65e81d90f4 | ||
|
|
4fb9637e2d | ||
|
|
6b3726dbd5 | ||
|
|
c5b0f2c76c | ||
|
|
ea18f9e28a | ||
|
|
d229c75978 | ||
|
|
ecbe3c3e4f | ||
|
|
38a5cb968b | ||
|
|
a30ee9e961 | ||
|
|
eba27004dc | ||
|
|
09849acd2f | ||
|
|
19e9047a39 | ||
|
|
82b6bdd9ad | ||
|
|
5764b4dac1 | ||
|
|
3affd67d24 | ||
|
|
d5a98a0f9e | ||
|
|
9629cc7226 | ||
|
|
06415ad869 | ||
|
|
3f872da435 | ||
|
|
bc1d5a9825 | ||
|
|
72c8ed9a00 | ||
|
|
3fec433846 | ||
|
|
3b572702d7 | ||
|
|
febdc0b48d | ||
|
|
8773bb2f62 | ||
|
|
55ad70fabc | ||
|
|
23dcd62672 | ||
|
|
42371d7ce3 | ||
|
|
4116746be3 | ||
|
|
b2b2856ac0 | ||
|
|
b65bca213f | ||
|
|
7a72e2cbb7 | ||
|
|
3a73398a1e | ||
|
|
cce20d9230 | ||
|
|
8b72c91951 | ||
|
|
b0d8e47a75 | ||
|
|
85824faa2b | ||
|
|
8a507339af | ||
|
|
4636849118 | ||
|
|
cc52cae9fc | ||
|
|
b6bb4d2c8b | ||
|
|
5032676bd2 | ||
|
|
146b37f8cd | ||
|
|
47b3a2d09e | ||
|
|
f2306b334b | ||
|
|
3affa65571 | ||
|
|
6f3be16826 | ||
|
|
07ce76537b | ||
|
|
a1fb38b8cb | ||
|
|
1b52a99662 | ||
|
|
f5facfa6b9 | ||
|
|
21c6144dc6 | ||
|
|
2c155f0e42 | ||
|
|
33cc0a8ab5 | ||
|
|
1e65a7e52b | ||
|
|
14e2dbe1aa | ||
|
|
a823a6ac56 | ||
|
|
cfc4e001ba | ||
|
|
496cc855f6 | ||
|
|
bfb2db07a5 | ||
|
|
9f4f868246 | ||
|
|
f91f02f786 | ||
|
|
1da72e3e7a | ||
|
|
2e2c0e087d | ||
|
|
0c72f58ab0 | ||
|
|
d9d969cb79 | ||
|
|
8d685d1f73 | ||
|
|
e406620044 | ||
|
|
a315674421 | ||
|
|
8d953f210f | ||
|
|
7439ef07ad | ||
|
|
fc4b4de6ed | ||
|
|
96e4733f87 | ||
|
|
8b6731a289 | ||
|
|
f93ac68e9c | ||
|
|
439df4879c | ||
|
|
83b6ce8574 | ||
|
|
6ea07bc13b | ||
|
|
a15624690b | ||
|
|
974bb85fb7 | ||
|
|
5dc80601b2 | ||
|
|
df2e53bc40 | ||
|
|
6e685bbc32 | ||
|
|
0b3ca0de96 | ||
|
|
aed3f1f141 | ||
|
|
1a7981e919 | ||
|
|
4add805371 | ||
|
|
66c18317b7 | ||
|
|
9edbb885b4 | ||
|
|
c67c80d6bc | ||
|
|
0399531e0c | ||
|
|
54426556d9 | ||
|
|
fa74ee1755 | ||
|
|
eb53af12c4 | ||
|
|
38efcb99c0 | ||
|
|
a92d914674 | ||
|
|
219a698aa5 | ||
|
|
5bed6448dc | ||
|
|
6d2a8ca3d3 | ||
|
|
bfb2e0043d | ||
|
|
922101b12c | ||
|
|
9ff41a0c4a | ||
|
|
071f187605 | ||
|
|
ab6cd0e89c | ||
|
|
97c0dad7ff | ||
|
|
bc0917a190 | ||
|
|
5718acecff |
No files matched your search
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
:position: before
|
||||||
|
:position_in_additional_file_patterns: before
|
||||||
|
:position_in_class: before
|
||||||
|
:position_in_factory: before
|
||||||
|
:position_in_fixture: before
|
||||||
|
:position_in_routes: before
|
||||||
|
:position_in_serializer: before
|
||||||
|
:position_in_test: before
|
||||||
|
:classified_sort: true
|
||||||
|
:exclude_controllers: true
|
||||||
|
:exclude_factories: true
|
||||||
|
:exclude_fixtures: false
|
||||||
|
:exclude_helpers: true
|
||||||
|
:exclude_scaffolds: true
|
||||||
|
:exclude_serializers: false
|
||||||
|
:exclude_sti_subclasses: false
|
||||||
|
:exclude_tests: false
|
||||||
|
:force: false
|
||||||
|
:format_markdown: false
|
||||||
|
:format_rdoc: false
|
||||||
|
:format_yard: false
|
||||||
|
:frozen: false
|
||||||
|
:ignore_model_sub_dir: false
|
||||||
|
:ignore_unknown_models: false
|
||||||
|
:include_version: false
|
||||||
|
:show_check_constraints: false
|
||||||
|
:show_complete_foreign_keys: false
|
||||||
|
:show_foreign_keys: true
|
||||||
|
:show_indexes: true
|
||||||
|
:simple_indexes: false
|
||||||
|
:sort: false
|
||||||
|
:timestamp: false
|
||||||
|
:trace: false
|
||||||
|
:with_comment: true
|
||||||
|
:with_column_comments: true
|
||||||
|
:with_table_comments: true
|
||||||
|
:active_admin: false
|
||||||
|
:command:
|
||||||
|
:debug: false
|
||||||
|
:hide_default_column_types: ''
|
||||||
|
:hide_limit_column_types: ''
|
||||||
|
:ignore_columns:
|
||||||
|
:ignore_routes:
|
||||||
|
:models: true
|
||||||
|
:routes: false
|
||||||
|
:skip_on_db_migrate: false
|
||||||
|
:target_action: :do_annotations
|
||||||
|
:wrapper:
|
||||||
|
:wrapper_close:
|
||||||
|
:wrapper_open:
|
||||||
|
:classes_default_to_s: []
|
||||||
|
:additional_file_patterns: []
|
||||||
|
:model_dir:
|
||||||
|
- app/models
|
||||||
|
:require: []
|
||||||
|
:root_dir:
|
||||||
|
- ''
|
||||||
@@ -7,13 +7,13 @@ jobs:
|
|||||||
name: Rubocop
|
name: Rubocop
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Install Ruby
|
- name: Install Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 3.4.1
|
ruby-version: 3.4.1
|
||||||
- name: Cache gems
|
- name: Cache gems
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: vendor/bundle
|
path: vendor/bundle
|
||||||
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
@@ -31,13 +31,13 @@ jobs:
|
|||||||
name: Erblint
|
name: Erblint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Install Ruby
|
- name: Install Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 3.4.1
|
ruby-version: 3.4.1
|
||||||
- name: Cache gems
|
- name: Cache gems
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: vendor/bundle
|
path: vendor/bundle
|
||||||
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
@@ -49,13 +49,13 @@ jobs:
|
|||||||
bundle config path vendor/bundle
|
bundle config path vendor/bundle
|
||||||
bundle install --jobs 4 --retry 4
|
bundle install --jobs 4 --retry 4
|
||||||
- name: Run Erblint
|
- name: Run Erblint
|
||||||
run: bundle exec erblint ./app
|
run: bundle exec erb_lint ./app
|
||||||
|
|
||||||
eslint:
|
eslint:
|
||||||
name: ESLint
|
name: ESLint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
- name: Cache directory path
|
- name: Cache directory path
|
||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
- uses: actions/cache@v1
|
- uses: actions/cache@v4
|
||||||
id: yarn-cache
|
id: yarn-cache
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
@@ -77,6 +77,33 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
./node_modules/eslint/bin/eslint.js "app/javascript/**/*.js"
|
./node_modules/eslint/bin/eslint.js "app/javascript/**/*.js"
|
||||||
|
|
||||||
|
brakeman:
|
||||||
|
name: Brakeman
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: 3.4.1
|
||||||
|
- name: Cache gems
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: vendor/bundle
|
||||||
|
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gem-
|
||||||
|
- name: Install gems
|
||||||
|
run: |
|
||||||
|
gem install bundler
|
||||||
|
bundle config path vendor/bundle
|
||||||
|
bundle install --jobs 4 --retry 4
|
||||||
|
yarn install
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libvips
|
||||||
|
- name: Run Brakeman
|
||||||
|
run: bundle exec brakeman -q --exit-on-warn
|
||||||
|
|
||||||
rspec:
|
rspec:
|
||||||
name: RSpec
|
name: RSpec
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -96,7 +123,7 @@ jobs:
|
|||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Install Ruby
|
- name: Install Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
@@ -110,12 +137,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
chrome-version: 125
|
chrome-version: 125
|
||||||
- name: Cache node_modules
|
- name: Cache node_modules
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
|
||||||
- name: Cache gems
|
- name: Cache gems
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: vendor/bundle
|
path: vendor/bundle
|
||||||
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -25,22 +25,22 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Create .version file
|
- name: Create .version file
|
||||||
run: echo ${{ github.ref_name }} > .version
|
run: echo ${{ github.ref_name }} > .version
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM ruby:3.4.1-alpine as fonts
|
FROM ruby:3.4.1-alpine AS fonts
|
||||||
|
|
||||||
WORKDIR /fonts
|
WORKDIR /fonts
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ RUN apk --no-cache add fontforge wget && wget https://github.com/satbyy/go-noto-
|
|||||||
|
|
||||||
RUN fontforge -lang=py -c 'font1 = fontforge.open("FreeSans.ttf"); font2 = fontforge.open("NotoSansSymbols2-Regular.ttf"); font1.mergeFonts(font2); font1.generate("FreeSans.ttf")'
|
RUN fontforge -lang=py -c 'font1 = fontforge.open("FreeSans.ttf"); font2 = fontforge.open("NotoSansSymbols2-Regular.ttf"); font1.mergeFonts(font2); font1.generate("FreeSans.ttf")'
|
||||||
|
|
||||||
FROM ruby:3.4.1-alpine as webpack
|
FROM ruby:3.4.1-alpine AS webpack
|
||||||
|
|
||||||
ENV RAILS_ENV=production
|
ENV RAILS_ENV=production
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
@@ -32,7 +32,7 @@ COPY ./app/views ./app/views
|
|||||||
|
|
||||||
RUN echo "gem 'shakapacker'" > Gemfile && ./bin/shakapacker
|
RUN echo "gem 'shakapacker'" > Gemfile && ./bin/shakapacker
|
||||||
|
|
||||||
FROM ruby:3.4.1-alpine as app
|
FROM ruby:3.4.1-alpine AS app
|
||||||
|
|
||||||
ENV RAILS_ENV=production
|
ENV RAILS_ENV=production
|
||||||
ENV BUNDLE_WITHOUT="development:test"
|
ENV BUNDLE_WITHOUT="development:test"
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ gem 'twitter_cldr', require: false
|
|||||||
gem 'tzinfo-data'
|
gem 'tzinfo-data'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'annotate'
|
|
||||||
gem 'better_html'
|
gem 'better_html'
|
||||||
gem 'bullet'
|
gem 'bullet'
|
||||||
gem 'debug'
|
gem 'debug'
|
||||||
@@ -63,6 +62,9 @@ group :development, :test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
gem 'annotaterb'
|
||||||
|
gem 'brakeman', require: false
|
||||||
|
gem 'foreman', require: false
|
||||||
gem 'letter_opener_web'
|
gem 'letter_opener_web'
|
||||||
gem 'web-console'
|
gem 'web-console'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -74,9 +74,7 @@ GEM
|
|||||||
uri (>= 0.13.1)
|
uri (>= 0.13.1)
|
||||||
addressable (2.8.7)
|
addressable (2.8.7)
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
annotate (2.6.5)
|
annotaterb (4.14.0)
|
||||||
activerecord (>= 2.3.0)
|
|
||||||
rake (>= 0.8.7)
|
|
||||||
arabic-letter-connector (0.1.1)
|
arabic-letter-connector (0.1.1)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
aws-eventstream (1.3.0)
|
aws-eventstream (1.3.0)
|
||||||
@@ -120,6 +118,8 @@ GEM
|
|||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.18.4)
|
bootsnap (1.18.4)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
|
brakeman (7.0.0)
|
||||||
|
racc
|
||||||
builder (3.3.0)
|
builder (3.3.0)
|
||||||
bullet (8.0.0)
|
bullet (8.0.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
@@ -223,9 +223,13 @@ GEM
|
|||||||
concurrent-ruby (~> 1.1)
|
concurrent-ruby (~> 1.1)
|
||||||
webrick (~> 1.7)
|
webrick (~> 1.7)
|
||||||
websocket-driver (~> 0.7)
|
websocket-driver (~> 0.7)
|
||||||
ffi (1.17.0)
|
ffi (1.17.1)
|
||||||
ffi (1.17.0-aarch64-linux-gnu)
|
ffi (1.17.1-aarch64-linux-gnu)
|
||||||
ffi (1.17.0-x86_64-linux-gnu)
|
ffi (1.17.1-aarch64-linux-musl)
|
||||||
|
ffi (1.17.1-arm64-darwin)
|
||||||
|
ffi (1.17.1-x86_64-linux-gnu)
|
||||||
|
ffi (1.17.1-x86_64-linux-musl)
|
||||||
|
foreman (0.88.1)
|
||||||
geom2d (0.4.1)
|
geom2d (0.4.1)
|
||||||
globalid (1.2.1)
|
globalid (1.2.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
@@ -324,7 +328,7 @@ GEM
|
|||||||
mysql2 (0.5.6)
|
mysql2 (0.5.6)
|
||||||
net-http-persistent (4.0.5)
|
net-http-persistent (4.0.5)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.5.3)
|
net-imap (0.5.6)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-pop (0.1.2)
|
net-pop (0.1.2)
|
||||||
@@ -334,12 +338,18 @@ GEM
|
|||||||
net-smtp (0.5.0)
|
net-smtp (0.5.0)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.4)
|
nio4r (2.7.4)
|
||||||
nokogiri (1.17.2)
|
nokogiri (1.18.5)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.17.2-aarch64-linux)
|
nokogiri (1.18.5-aarch64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.17.2-x86_64-linux)
|
nokogiri (1.18.5-aarch64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.5-arm64-darwin)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.5-x86_64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.5-x86_64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oj (3.16.8)
|
oj (3.16.8)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
@@ -377,7 +387,7 @@ GEM
|
|||||||
puma (6.5.0)
|
puma (6.5.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (3.1.8)
|
rack (3.1.12)
|
||||||
rack-proxy (0.7.7)
|
rack-proxy (0.7.7)
|
||||||
rack
|
rack
|
||||||
rack-session (2.0.0)
|
rack-session (2.0.0)
|
||||||
@@ -520,10 +530,13 @@ GEM
|
|||||||
simplecov-html (0.13.1)
|
simplecov-html (0.13.1)
|
||||||
simplecov_json_formatter (0.1.4)
|
simplecov_json_formatter (0.1.4)
|
||||||
smart_properties (1.17.0)
|
smart_properties (1.17.0)
|
||||||
sqlite3 (2.4.1)
|
sqlite3 (2.5.0)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
sqlite3 (2.4.1-aarch64-linux-gnu)
|
sqlite3 (2.5.0-aarch64-linux-gnu)
|
||||||
sqlite3 (2.4.1-x86_64-linux-gnu)
|
sqlite3 (2.5.0-aarch64-linux-musl)
|
||||||
|
sqlite3 (2.5.0-arm64-darwin)
|
||||||
|
sqlite3 (2.5.0-x86_64-linux-gnu)
|
||||||
|
sqlite3 (2.5.0-x86_64-linux-musl)
|
||||||
stringio (3.1.2)
|
stringio (3.1.2)
|
||||||
strip_attributes (1.14.1)
|
strip_attributes (1.14.1)
|
||||||
activemodel (>= 3.0, < 9.0)
|
activemodel (>= 3.0, < 9.0)
|
||||||
@@ -546,7 +559,7 @@ GEM
|
|||||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
||||||
unicode-emoji (4.0.4)
|
unicode-emoji (4.0.4)
|
||||||
uniform_notifier (1.16.0)
|
uniform_notifier (1.16.0)
|
||||||
uri (1.0.2)
|
uri (1.0.3)
|
||||||
useragent (0.16.11)
|
useragent (0.16.11)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
@@ -569,17 +582,21 @@ GEM
|
|||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
aarch64-linux
|
aarch64-linux
|
||||||
|
aarch64-linux-musl
|
||||||
|
arm64-darwin
|
||||||
ruby
|
ruby
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
x86_64-linux-musl
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
annotate
|
annotaterb
|
||||||
arabic-letter-connector
|
arabic-letter-connector
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
aws-sdk-secretsmanager
|
aws-sdk-secretsmanager
|
||||||
azure-storage-blob
|
azure-storage-blob
|
||||||
better_html
|
better_html
|
||||||
bootsnap
|
bootsnap
|
||||||
|
brakeman
|
||||||
bullet
|
bullet
|
||||||
cancancan
|
cancancan
|
||||||
capybara
|
capybara
|
||||||
@@ -595,6 +612,7 @@ DEPENDENCIES
|
|||||||
faker
|
faker
|
||||||
faraday
|
faraday
|
||||||
faraday-follow_redirects
|
faraday-follow_redirects
|
||||||
|
foreman
|
||||||
google-cloud-storage
|
google-cloud-storage
|
||||||
hexapdf
|
hexapdf
|
||||||
image_processing
|
image_processing
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class AccountConfigsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def account_config_params
|
def account_config_params
|
||||||
params.required(:account_config).permit!.tap do |attrs|
|
params.required(:account_config).permit(:key, :value, { value: {} }, { value: [] }).tap do |attrs|
|
||||||
attrs[:value] = attrs[:value] == '1' if attrs[:value].in?(%w[1 0])
|
attrs[:value] = attrs[:value] == '1' if attrs[:value].in?(%w[1 0])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ module Api
|
|||||||
last_submitter = @submission.submitters.max_by(&:completed_at)
|
last_submitter = @submission.submitters.max_by(&:completed_at)
|
||||||
|
|
||||||
if last_submitter.documents_attachments.blank?
|
if last_submitter.documents_attachments.blank?
|
||||||
last_submitter.documents_attachments = Submissions::EnsureResultGenerated.call(submitter)
|
last_submitter.documents_attachments = Submissions::EnsureResultGenerated.call(last_submitter)
|
||||||
end
|
end
|
||||||
|
|
||||||
last_submitter.documents_attachments
|
last_submitter.documents_attachments
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ module Api
|
|||||||
message: %i[subject body],
|
message: %i[subject body],
|
||||||
submitters: [[:send_email, :send_sms, :completed_redirect_url, :uuid, :name, :email, :role,
|
submitters: [[:send_email, :send_sms, :completed_redirect_url, :uuid, :name, :email, :role,
|
||||||
:completed, :phone, :application_key, :external_id, :reply_to, :go_to_last,
|
:completed, :phone, :application_key, :external_id, :reply_to, :go_to_last,
|
||||||
{ metadata: {}, values: {}, readonly_fields: [], message: %i[subject body],
|
{ metadata: {}, values: {}, roles: [], readonly_fields: [], message: %i[subject body],
|
||||||
fields: [:name, :uuid, :default_value, :value, :title, :description,
|
fields: [:name, :uuid, :default_value, :value, :title, :description,
|
||||||
:readonly, :validation_pattern, :invalid_message,
|
:readonly, :validation_pattern, :invalid_message,
|
||||||
{ default_value: [], value: [], preferences: {} }] }]]
|
{ default_value: [], value: [], preferences: {} }] }]]
|
||||||
|
|||||||
@@ -146,9 +146,15 @@ module Api
|
|||||||
|
|
||||||
if attrs[:completed]
|
if attrs[:completed]
|
||||||
submitter.values = Submitters::SubmitValues.merge_default_values(submitter)
|
submitter.values = Submitters::SubmitValues.merge_default_values(submitter)
|
||||||
submitter.values = Submitters::SubmitValues.merge_formula_values(submitter)
|
|
||||||
submitter.values = Submitters::SubmitValues.maybe_remove_condition_values(submitter)
|
submitter.values = Submitters::SubmitValues.maybe_remove_condition_values(submitter)
|
||||||
|
|
||||||
|
formula_values = Submitters::SubmitValues.build_formula_values(submitter)
|
||||||
|
|
||||||
|
if formula_values.present?
|
||||||
|
submitter.values = submitter.values.merge(formula_values)
|
||||||
|
submitter.values = Submitters::SubmitValues.maybe_remove_condition_values(submitter)
|
||||||
|
end
|
||||||
|
|
||||||
submitter.values = submitter.values.transform_values do |v|
|
submitter.values = submitter.values.transform_values do |v|
|
||||||
v == '{{date}}' ? Time.current.in_time_zone(submitter.account.timezone).to_date.to_s : v
|
v == '{{date}}' ? Time.current.in_time_zone(submitter.account.timezone).to_date.to_s : v
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ module Api
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
rescue HexaPDF::MalformedPDFError
|
||||||
|
render json: { error: 'Malformed PDF' }, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
|
|||||||
redirect_to request.referer, alert: 'Too many requests', status: :too_many_requests
|
redirect_to request.referer, alert: 'Too many requests', status: :too_many_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
if Rails.env.production?
|
if Rails.env.production? || Rails.env.test?
|
||||||
rescue_from CanCan::AccessDenied do |e|
|
rescue_from CanCan::AccessDenied do |e|
|
||||||
Rollbar.warning(e) if defined?(Rollbar)
|
Rollbar.warning(e) if defined?(Rollbar)
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class NotificationsSettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def email_config_params
|
def email_config_params
|
||||||
params.require(:account_config).permit!.tap do |attrs|
|
params.require(:account_config).permit(:key, :value, { value: {} }, { value: [] }).tap do |attrs|
|
||||||
attrs[:key] = nil unless attrs[:key].in?([AccountConfig::BCC_EMAILS, AccountConfig::SUBMITTER_REMINDERS])
|
attrs[:key] = nil unless attrs[:key].in?([AccountConfig::BCC_EMAILS, AccountConfig::SUBMITTER_REMINDERS])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class PersonalizationSettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def account_config_params
|
def account_config_params
|
||||||
attrs = params.require(:account_config).permit!
|
attrs = params.require(:account_config).permit(:key, :value, { value: {} }, { value: [] })
|
||||||
|
|
||||||
return attrs if attrs[:value].is_a?(String)
|
return attrs if attrs[:value].is_a?(String)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class PwaController < ActionController::Base
|
||||||
|
end
|
||||||
@@ -9,8 +9,6 @@ class SendSubmissionEmailController < ApplicationController
|
|||||||
|
|
||||||
SEND_DURATION = 30.minutes
|
SEND_DURATION = 30.minutes
|
||||||
|
|
||||||
def success; end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@submitter =
|
@submitter =
|
||||||
if params[:template_slug]
|
if params[:template_slug]
|
||||||
@@ -31,7 +29,7 @@ class SendSubmissionEmailController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |f|
|
respond_to do |f|
|
||||||
f.html { redirect_to success_send_submission_email_index_path }
|
f.html { render :success }
|
||||||
f.json { head :ok }
|
f.json { head :ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class StartFormController < ApplicationController
|
|||||||
redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email])
|
redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email])
|
||||||
else
|
else
|
||||||
if filter_undefined_submitters(@template).size > 1 && @submitter.new_record?
|
if filter_undefined_submitters(@template).size > 1 && @submitter.new_record?
|
||||||
@error_message = I18n.t('not_found')
|
@error_message = multiple_submitters_error_message
|
||||||
|
|
||||||
return render :show
|
return render :show
|
||||||
end
|
end
|
||||||
@@ -34,6 +34,8 @@ class StartFormController < ApplicationController
|
|||||||
assign_submission_attributes(@submitter, @template)
|
assign_submission_attributes(@submitter, @template)
|
||||||
|
|
||||||
Submissions::AssignDefinedSubmitters.call(@submitter.submission)
|
Submissions::AssignDefinedSubmitters.call(@submitter.submission)
|
||||||
|
else
|
||||||
|
@submitter.assign_attributes(ip: request.remote_ip, ua: request.user_agent)
|
||||||
end
|
end
|
||||||
|
|
||||||
if @submitter.save
|
if @submitter.save
|
||||||
@@ -65,8 +67,9 @@ class StartFormController < ApplicationController
|
|||||||
.order(id: :desc)
|
.order(id: :desc)
|
||||||
.where(declined_at: nil)
|
.where(declined_at: nil)
|
||||||
.where(external_id: nil)
|
.where(external_id: nil)
|
||||||
|
.where(ip: [nil, request.remote_ip])
|
||||||
.then { |rel| params[:resubmit].present? ? rel.where(completed_at: nil) : rel }
|
.then { |rel| params[:resubmit].present? ? rel.where(completed_at: nil) : rel }
|
||||||
.find_or_initialize_by(**submitter_params.compact_blank)
|
.find_or_initialize_by(email: submitter_params[:email], **submitter_params.compact_blank)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_submission_attributes(submitter, template)
|
def assign_submission_attributes(submitter, template)
|
||||||
@@ -114,4 +117,12 @@ class StartFormController < ApplicationController
|
|||||||
|
|
||||||
@template = Template.find_by!(slug:)
|
@template = Template.find_by!(slug:)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def multiple_submitters_error_message
|
||||||
|
if current_user&.account_id == @template.account_id
|
||||||
|
helpers.t('this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html')
|
||||||
|
else
|
||||||
|
I18n.t('not_found')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SubmissionEventsController < ApplicationController
|
||||||
|
SUBMISSION_EVENT_ICONS = {
|
||||||
|
'view_form' => 'eye',
|
||||||
|
'start_form' => 'player_play',
|
||||||
|
'complete_form' => 'check',
|
||||||
|
'send_email' => 'mail_forward',
|
||||||
|
'click_email' => 'hand_click',
|
||||||
|
'api_complete_form' => 'check',
|
||||||
|
'send_reminder_email' => 'mail_forward',
|
||||||
|
'send_2fa_sms' => '2fa',
|
||||||
|
'send_sms' => 'send',
|
||||||
|
'phone_verified' => 'phone_check',
|
||||||
|
'click_sms' => 'hand_click',
|
||||||
|
'decline_form' => 'x',
|
||||||
|
'start_verification' => 'player_play',
|
||||||
|
'complete_verification' => 'check',
|
||||||
|
'invite_party' => 'user_plus'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
load_and_authorize_resource :submission
|
||||||
|
|
||||||
|
def index; end
|
||||||
|
end
|
||||||
@@ -20,7 +20,7 @@ class SubmissionsPreviewController < ApplicationController
|
|||||||
|
|
||||||
@submission ||= Submission.find_by!(slug: params[:slug])
|
@submission ||= Submission.find_by!(slug: params[:slug])
|
||||||
|
|
||||||
raise ActionController::RoutingError if @submission.account.archived_at?
|
raise ActionController::RoutingError, I18n.t('not_found') if @submission.account.archived_at?
|
||||||
|
|
||||||
if !@submission.submitters.all?(&:completed_at?) && !signature_valid &&
|
if !@submission.submitters.all?(&:completed_at?) && !signature_valid &&
|
||||||
(!current_user || !current_ability.can?(:read, @submission))
|
(!current_user || !current_ability.can?(:read, @submission))
|
||||||
@@ -40,6 +40,7 @@ class SubmissionsPreviewController < ApplicationController
|
|||||||
|
|
||||||
def completed
|
def completed
|
||||||
@submission = Submission.find_by!(slug: params[:submissions_preview_slug])
|
@submission = Submission.find_by!(slug: params[:submissions_preview_slug])
|
||||||
|
@template = @submission.template
|
||||||
|
|
||||||
render :completed, layout: 'form'
|
render :completed, layout: 'form'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ class TemplatesDebugController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
attachment = @template.documents.first
|
attachment = @template.documents.first
|
||||||
|
|
||||||
pdf = HexaPDF::Document.new(io: StringIO.new(attachment.download))
|
data = attachment.download
|
||||||
|
pdf = HexaPDF::Document.new(io: StringIO.new(data))
|
||||||
|
|
||||||
fields = Templates::FindAcroFields.call(pdf, attachment)
|
fields = Templates::FindAcroFields.call(pdf, attachment, data)
|
||||||
|
|
||||||
attachment.metadata['pdf'] ||= {}
|
attachment.metadata['pdf'] ||= {}
|
||||||
attachment.metadata['pdf']['fields'] = fields
|
attachment.metadata['pdf']['fields'] = fields
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ class TemplatesPreferencesController < ApplicationController
|
|||||||
submitters_order
|
submitters_order
|
||||||
completed_notification_email_subject completed_notification_email_body
|
completed_notification_email_subject completed_notification_email_body
|
||||||
completed_notification_email_enabled completed_notification_email_attach_audit] +
|
completed_notification_email_enabled completed_notification_email_attach_audit] +
|
||||||
[completed_message: %i[title body]]
|
[completed_message: %i[title body],
|
||||||
|
submitters: [%i[uuid request_email_subject request_email_body]]]
|
||||||
).tap do |attrs|
|
).tap do |attrs|
|
||||||
|
attrs[:preferences].delete(:submitters) if params[:request_email_per_submitter] != '1'
|
||||||
attrs[:preferences] = attrs[:preferences].transform_values do |value|
|
attrs[:preferences] = attrs[:preferences].transform_values do |value|
|
||||||
if %w[true false].include?(value)
|
if %w[true false].include?(value)
|
||||||
value == 'true'
|
value == 'true'
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class UserConfigsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def user_config_params
|
def user_config_params
|
||||||
params.required(:user_config).permit!.tap do |attrs|
|
params.required(:user_config).permit(:key, :value, { value: {} }, { value: [] }).tap do |attrs|
|
||||||
attrs[:value] = attrs[:value] == '1' if attrs[:value].in?(%w[1 0])
|
attrs[:value] = attrs[:value] == '1' if attrs[:value].in?(%w[1 0])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -45,17 +45,16 @@ class UsersController < ApplicationController
|
|||||||
return redirect_to settings_users_path, notice: I18n.t('unable_to_update_user') if Docuseal.demo?
|
return redirect_to settings_users_path, notice: I18n.t('unable_to_update_user') if Docuseal.demo?
|
||||||
|
|
||||||
attrs = user_params.compact_blank.merge(user_params.slice(:archived_at))
|
attrs = user_params.compact_blank.merge(user_params.slice(:archived_at))
|
||||||
attrs.delete(:role) if !role_valid?(attrs[:role]) || current_user == @user
|
|
||||||
|
|
||||||
if params.dig(:user, :account_id).present?
|
if params.dig(:user, :account_id).present?
|
||||||
account = Account.accessible_by(current_ability).find(params[:user][:account_id])
|
account = Account.accessible_by(current_ability).find(params.dig(:user, :account_id))
|
||||||
|
|
||||||
authorize!(:manage, account)
|
authorize!(:manage, account)
|
||||||
|
|
||||||
@user.account = account
|
@user.account = account
|
||||||
end
|
end
|
||||||
|
|
||||||
if @user.update(attrs)
|
if @user.update(attrs.except(current_user == @user ? :role : nil))
|
||||||
redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_updated')
|
redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_updated')
|
||||||
else
|
else
|
||||||
render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity
|
render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity
|
||||||
@@ -84,8 +83,11 @@ class UsersController < ApplicationController
|
|||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
if params.key?(:user)
|
if params.key?(:user)
|
||||||
params.require(:user).permit(:email, :first_name, :last_name, :password,
|
permitted_params = %i[email first_name last_name password archived_at]
|
||||||
:role, :archived_at, :account_id)
|
|
||||||
|
permitted_params << :role if role_valid?(params.dig(:user, :role))
|
||||||
|
|
||||||
|
params.require(:user).permit(permitted_params)
|
||||||
else
|
else
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ export default class extends HTMLElement {
|
|||||||
this.resize()
|
this.resize()
|
||||||
|
|
||||||
this.textarea.addEventListener('input', () => this.resize())
|
this.textarea.addEventListener('input', () => this.resize())
|
||||||
|
|
||||||
|
this.observeVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
resize () {
|
resize () {
|
||||||
@@ -11,6 +13,28 @@ export default class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observeVisibility () {
|
||||||
|
this.observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
this.resize()
|
||||||
|
this.observer.unobserve(this.textarea)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0.1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
this.observer.observe(this.textarea)
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback () {
|
||||||
|
this.observer.unobserve(this.textarea)
|
||||||
|
}
|
||||||
|
|
||||||
get textarea () {
|
get textarea () {
|
||||||
return this.querySelector('textarea')
|
return this.querySelector('textarea')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="field-area flex absolute lg:text-base -outline-offset-1"
|
class="flex absolute lg:text-base -outline-offset-1 field-area"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
:style="computedStyle"
|
:style="computedStyle"
|
||||||
:class="{ 'font-serif': field.preferences?.font === 'Times', 'text-[1.6vw] lg:text-base': !textOverflowChars, 'text-[1.0vw] lg:text-xs': textOverflowChars, 'cursor-default': !submittable, 'border border-red-100 bg-red-100 cursor-pointer': submittable, 'border border-red-100': !isActive && submittable, 'bg-opacity-80': !isActive && !isValueSet && submittable, 'field-area-active outline-red-500 outline-dashed outline-2 z-10': isActive && submittable, 'bg-opacity-40': (isActive || isValueSet) && submittable }"
|
:class="{ 'text-[1.6vw] lg:text-base': !textOverflowChars, 'text-[1.0vw] lg:text-xs': textOverflowChars, 'cursor-default': !submittable, 'border border-red-100 bg-red-100 cursor-pointer': submittable, 'border border-red-100': !isActive && submittable, 'bg-opacity-80': !isActive && !isValueSet && submittable, 'outline-red-500 outline-dashed outline-2 z-10 field-area-active': isActive && submittable, 'bg-opacity-40': (isActive || isValueSet) && submittable }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="(!withFieldPlaceholder || !field.name || field.type === 'cells') && !isActive && !isValueSet && field.type !== 'checkbox' && submittable && !area.option_uuid"
|
v-if="(!withFieldPlaceholder || !field.name || field.type === 'cells') && !isActive && !isValueSet && field.type !== 'checkbox' && submittable && !area.option_uuid"
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
<div
|
<div
|
||||||
v-else-if="field.type === 'cells'"
|
v-else-if="field.type === 'cells'"
|
||||||
class="w-full flex items-center"
|
class="w-full flex items-center"
|
||||||
:class="{ 'justify-end': field.preferences?.align === 'right' }"
|
:class="{ 'justify-end': field.preferences?.align === 'right', ...fontClasses }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(char, index) in modelValue"
|
v-for="(char, index) in modelValue"
|
||||||
@@ -184,17 +184,23 @@
|
|||||||
ref="textContainer"
|
ref="textContainer"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="flex items-center px-0.5 w-full"
|
class="flex items-center px-0.5 w-full"
|
||||||
:class="alignClasses[field.preferences?.align]"
|
:class="{ ...alignClasses, ...fontClasses }"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="field && field.name && withFieldPlaceholder && !modelValue && modelValue !== 0"
|
v-if="field && field.name && withFieldPlaceholder && !modelValue && modelValue !== 0"
|
||||||
class="whitespace-pre-wrap text-gray-400"
|
class="whitespace-pre-wrap text-gray-400"
|
||||||
:class="{ 'w-full': field.preferences?.align }"
|
:class="{ 'w-full': field.preferences?.align }"
|
||||||
>{{ field.name }}</span>
|
>{{ field.name }}</span>
|
||||||
<span v-else-if="Array.isArray(modelValue)">
|
<span
|
||||||
|
v-else-if="Array.isArray(modelValue)"
|
||||||
|
:class="{ 'w-full': field.preferences?.align }"
|
||||||
|
>
|
||||||
{{ modelValue.join(', ') }}
|
{{ modelValue.join(', ') }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="field.type === 'date'">
|
<span
|
||||||
|
v-else-if="field.type === 'date'"
|
||||||
|
:class="{ 'w-full': field.preferences?.align }"
|
||||||
|
>
|
||||||
{{ formattedDate }}
|
{{ formattedDate }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
@@ -320,10 +326,26 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
alignClasses () {
|
alignClasses () {
|
||||||
|
if (!this.field.preferences) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
center: 'text-center',
|
'text-center': this.field.preferences.align === 'center',
|
||||||
left: 'text-left',
|
'text-left': this.field.preferences.align === 'left',
|
||||||
right: 'text-right'
|
'text-right': this.field.preferences.align === 'right'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fontClasses () {
|
||||||
|
if (!this.field.preferences) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'font-mono': this.field.preferences.font === 'Courier',
|
||||||
|
'font-serif': this.field.preferences.font === 'Times',
|
||||||
|
'font-bold': ['bold_italic', 'bold'].includes(this.field.preferences.font_type),
|
||||||
|
italic: ['bold_italic', 'italic'].includes(this.field.preferences.font_type)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
option () {
|
option () {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
:with-label="withLabel && !withFieldPlaceholder && step.length < 2"
|
:with-label="withLabel && !withFieldPlaceholder && step.length < 2"
|
||||||
:is-value-set="step.some((f) => f.uuid in values)"
|
:is-value-set="step.some((f) => f.uuid in values)"
|
||||||
:attachments-index="attachmentsIndex"
|
:attachments-index="attachmentsIndex"
|
||||||
@click="$emit('focus-step', stepIndex)"
|
@click="[$emit('focus-step', stepIndex), maybeScrollOnClick(field, area)]"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
@@ -109,6 +109,14 @@ export default {
|
|||||||
areaRefs: []
|
areaRefs: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isMobileContainer () {
|
||||||
|
const root = this.$root.$el.parentNode.getRootNode()
|
||||||
|
const container = root.body || root.querySelector('div')
|
||||||
|
|
||||||
|
return container.style.overflow === 'hidden'
|
||||||
|
}
|
||||||
|
},
|
||||||
beforeUpdate () {
|
beforeUpdate () {
|
||||||
this.areaRefs = []
|
this.areaRefs = []
|
||||||
},
|
},
|
||||||
@@ -121,14 +129,16 @@ export default {
|
|||||||
this.scrollIntoArea(field.areas[0])
|
this.scrollIntoArea(field.areas[0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
maybeScrollOnClick (field, area) {
|
||||||
|
if (['text', 'number', 'cells'].includes(field.type) && this.isMobileContainer) {
|
||||||
|
this.scrollIntoArea(area)
|
||||||
|
}
|
||||||
|
},
|
||||||
scrollIntoArea (area) {
|
scrollIntoArea (area) {
|
||||||
const areaRef = this.areaRefs.find((a) => a.area === area)
|
const areaRef = this.areaRefs.find((a) => a.area === area)
|
||||||
|
|
||||||
if (areaRef) {
|
if (areaRef) {
|
||||||
const root = this.$root.$el.parentNode.getRootNode()
|
if (this.isMobileContainer) {
|
||||||
const container = root.body || root.querySelector('div')
|
|
||||||
|
|
||||||
if (container.style.overflow === 'hidden') {
|
|
||||||
this.scrollInContainer(areaRef.$el)
|
this.scrollInContainer(areaRef.$el)
|
||||||
} else {
|
} else {
|
||||||
const targetRect = areaRef.$refs.scrollToElem.getBoundingClientRect()
|
const targetRect = areaRef.$refs.scrollToElem.getBoundingClientRect()
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
v-if="val"
|
v-if="val"
|
||||||
class="flex items-center space-x-1.5 w-full"
|
class="flex items-center space-x-1.5 w-full attachment-file-name"
|
||||||
:href="attachmentsIndex[val].url"
|
:href="attachmentsIndex[val].url"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
@@ -26,7 +26,10 @@
|
|||||||
{{ attachmentsIndex[val].filename }}
|
{{ attachmentsIndex[val].filename }}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<button @click.prevent="removeAttachment(val)">
|
<button
|
||||||
|
class="remove-attachment-button"
|
||||||
|
@click.prevent="removeAttachment(val)"
|
||||||
|
>
|
||||||
<IconTrashX
|
<IconTrashX
|
||||||
:width="18"
|
:width="18"
|
||||||
:heigh="19"
|
:heigh="19"
|
||||||
@@ -44,12 +47,12 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description && !modelValue.length"
|
v-if="field.description && !modelValue.length"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
<FileDropzone
|
<FileDropzone
|
||||||
:message="`${t('upload')} ${field.name || t('files')}${field.required ? '' : ` (${t('optional')})`}`"
|
:message="`${t('upload')} ${(field.title || field.name) || t('files')}${field.required ? '' : ` (${t('optional')})`}`"
|
||||||
:submitter-slug="submitterSlug"
|
:submitter-slug="submitterSlug"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:dry-run="dryRun"
|
:dry-run="dryRun"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="form_completed"
|
id="form_completed"
|
||||||
class="mx-auto max-w-md flex flex-col"
|
class="mx-auto max-w-md flex flex-col completed-form"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
>
|
>
|
||||||
<div class="font-medium text-2xl flex items-center space-x-1.5 mx-auto">
|
<div class="font-medium text-2xl flex items-center space-x-1.5 mx-auto">
|
||||||
@@ -10,13 +10,13 @@
|
|||||||
:width="30"
|
:width="30"
|
||||||
:height="30"
|
:height="30"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span class="completed-form-message-title">
|
||||||
{{ completedMessage.title || (hasSignatureFields ? (hasMultipleDocuments ? t('documents_have_been_signed') : t('document_has_been_signed')) : t('form_has_been_completed')) }}
|
{{ completedMessage.title || (hasSignatureFields ? (hasMultipleDocuments ? t('documents_have_been_signed') : t('document_has_been_signed')) : t('form_has_been_completed')) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="completedMessage.body"
|
v-if="completedMessage.body"
|
||||||
class="mt-2"
|
class="mt-2 completed-form-message-body"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
:string="completedMessage.body"
|
:string="completedMessage.body"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
v-if="completedButton.url"
|
v-if="completedButton.url"
|
||||||
:href="sanitizeHref(completedButton.url)"
|
:href="sanitizeHref(completedButton.url)"
|
||||||
rel="noopener noreferrer nofollow"
|
rel="noopener noreferrer nofollow"
|
||||||
class="white-button flex items-center w-full"
|
class="white-button flex items-center w-full completed-form-completed-button"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ completedButton.title || 'Back to Website' }}
|
{{ completedButton.title || 'Back to Website' }}
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
v-if="canSendEmail && !isDemo && withSendCopyButton"
|
v-if="canSendEmail && !isDemo && withSendCopyButton"
|
||||||
class="white-button !h-auto flex items-center space-x-1 w-full"
|
class="white-button !h-auto flex items-center space-x-1 w-full completed-form-send-copy-button"
|
||||||
:disabled="isSendingCopy"
|
:disabled="isSendingCopy"
|
||||||
@click.prevent="sendCopyToEmail"
|
@click.prevent="sendCopyToEmail"
|
||||||
>
|
>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="!isWebView && withDownloadButton"
|
v-if="!isWebView && withDownloadButton"
|
||||||
class="base-button flex items-center space-x-1 w-full"
|
class="base-button flex items-center space-x-1 w-full completed-form-download-button"
|
||||||
:disabled="isDownloading"
|
:disabled="isDownloading"
|
||||||
@click.prevent="download"
|
@click.prevent="download"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,26 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<div
|
<div
|
||||||
class="flex justify-between items-center w-full"
|
class="flex justify-between items-end w-full mb-3.5 sm:mb-4"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
|
v-if="showFieldNames"
|
||||||
:for="field.uuid"
|
:for="field.uuid"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 field-name-label"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
v-if="field.title"
|
v-if="field.title"
|
||||||
:string="field.title"
|
:string="field.title"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ field.name && showFieldNames ? field.name : t('date') }}
|
{{ field.name || t('date') }}
|
||||||
<template v-if="!field.required">
|
</template>
|
||||||
|
<template v-if="!field.required">
|
||||||
|
<span
|
||||||
|
class="ml-1"
|
||||||
|
:class="{ 'hidden sm:inline': (field.title || field.name || t('date') ).length > 10 }"
|
||||||
|
>
|
||||||
({{ t('optional') }})
|
({{ t('optional') }})
|
||||||
</template>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline btn-sm !normal-case font-normal"
|
class="btn btn-outline btn-sm !normal-case font-normal set-current-date-button"
|
||||||
@click.prevent="[setCurrentDate(), $emit('focus')]"
|
@click.prevent="[setCurrentDate(), $emit('focus')]"
|
||||||
>
|
>
|
||||||
<IconCalendarCheck :width="16" />
|
<IconCalendarCheck :width="16" />
|
||||||
@@ -29,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="w-full relative bg-base-300 hover:bg-base-200 rounded-md border border-base-content border-dashed"
|
class="w-full relative bg-base-300 hover:bg-base-200 rounded-md border border-base-content border-dashed file-dropzone"
|
||||||
:class="{ 'opacity-50': isLoading }"
|
:class="{ 'opacity-50': isLoading }"
|
||||||
>
|
>
|
||||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
|
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="!isFormVisible"
|
v-if="!isFormVisible"
|
||||||
id="expand_form_button"
|
id="expand_form_button"
|
||||||
class="btn btn-neutral flex text-white absolute bottom-0 w-full mb-3"
|
class="btn btn-neutral flex text-white absolute bottom-0 w-full mb-3 expand-form-button"
|
||||||
style="width: 96%; margin-left: 2%"
|
style="width: 96%; margin-left: 2%"
|
||||||
@click.prevent="[isFormVisible = true, scrollIntoField(currentField)]"
|
@click.prevent="[isFormVisible = true, scrollIntoField(currentField)]"
|
||||||
>
|
>
|
||||||
@@ -93,14 +93,15 @@
|
|||||||
<div
|
<div
|
||||||
v-show="isFormVisible"
|
v-show="isFormVisible"
|
||||||
id="form_container"
|
id="form_container"
|
||||||
class="shadow-md bg-base-100 absolute bottom-0 w-full border-base-200 border p-4 rounded"
|
class="shadow-md bg-base-100 absolute bottom-0 w-full border-base-200 border p-4 rounded form-container"
|
||||||
:class="{ 'md:bottom-4': isBreakpointMd }"
|
:class="{ 'md:bottom-4': isBreakpointMd }"
|
||||||
:style="{ backgroundColor: backgroundColor }"
|
:style="{ backgroundColor: backgroundColor }"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-if="!isCompleted"
|
v-if="!isCompleted"
|
||||||
id="minimize_form_button"
|
id="minimize_form_button"
|
||||||
class="absolute right-0 mr-2 mt-2 top-0 hidden md:block"
|
class="absolute right-0 top-0 minimize-form-button"
|
||||||
|
:class="currentField?.description?.length > 100 ? 'mr-1 mt-1 md:mr-2 md:mt-2': 'mr-2 mt-2 hidden md:block'"
|
||||||
:title="t('minimize')"
|
:title="t('minimize')"
|
||||||
@click.prevent="minimizeForm"
|
@click.prevent="minimizeForm"
|
||||||
>
|
>
|
||||||
@@ -118,7 +119,7 @@
|
|||||||
ref="form"
|
ref="form"
|
||||||
:action="submitPath"
|
:action="submitPath"
|
||||||
method="post"
|
method="post"
|
||||||
class="mx-auto"
|
class="mx-auto steps-form"
|
||||||
:style="{ maxWidth: isBreakpointMd ? '582px' : '' }"
|
:style="{ maxWidth: isBreakpointMd ? '582px' : '' }"
|
||||||
@submit.prevent="submitStep"
|
@submit.prevent="submitStep"
|
||||||
>
|
>
|
||||||
@@ -164,7 +165,7 @@
|
|||||||
v-if="showFieldNames && (currentField.name || currentField.title)"
|
v-if="showFieldNames && (currentField.name || currentField.title)"
|
||||||
:for="currentField.uuid"
|
:for="currentField.uuid"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !currentField.description }"
|
:class="{ 'mb-2': !currentField.description }"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
@@ -173,7 +174,11 @@
|
|||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ currentField.name }}
|
{{ currentField.name }}
|
||||||
<template v-if="!currentField.required">({{ t('optional') }})</template>
|
</template>
|
||||||
|
<template v-if="!currentField.required">
|
||||||
|
<span :class="{ 'hidden sm:inline': (currentField.title || currentField.name).length > 20 }">
|
||||||
|
({{ t('optional') }})
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
@@ -183,7 +188,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="currentField.description"
|
v-if="currentField.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="currentField.description" />
|
<MarkdownContent :string="currentField.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -221,7 +226,7 @@
|
|||||||
v-if="showFieldNames && (currentField.name || currentField.title)"
|
v-if="showFieldNames && (currentField.name || currentField.title)"
|
||||||
:for="currentField.uuid"
|
:for="currentField.uuid"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !currentField.description }"
|
:class="{ 'mb-2': !currentField.description }"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
@@ -230,13 +235,17 @@
|
|||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ currentField.name }}
|
{{ currentField.name }}
|
||||||
<template v-if="!currentField.required">({{ t('optional') }})</template>
|
</template>
|
||||||
|
<template v-if="!currentField.required">
|
||||||
|
<span :class="{ 'hidden sm:inline': (currentField.title || currentField.name).length > 20 }">
|
||||||
|
({{ t('optional') }})
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
v-if="currentField.description"
|
v-if="currentField.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="currentField.description" />
|
<MarkdownContent :string="currentField.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -261,7 +270,7 @@
|
|||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
:for="option.uuid"
|
:for="option.uuid"
|
||||||
class="flex items-center space-x-3"
|
class="flex items-center space-x-3 radio-label"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
:id="option.uuid"
|
:id="option.uuid"
|
||||||
@@ -295,7 +304,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="currentField.description"
|
v-if="currentField.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="currentField.description" />
|
<MarkdownContent :string="currentField.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -329,7 +338,7 @@
|
|||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
:for="field.uuid"
|
:for="field.uuid"
|
||||||
class="flex items-center space-x-3"
|
class="flex items-center space-x-3 checkbox-label"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
@@ -466,13 +475,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="(currentField.type !== 'payment' && currentField.type !== 'verification') || submittedValues[currentField.uuid]"
|
v-if="(currentField.type !== 'payment' && currentField.type !== 'verification') || submittedValues[currentField.uuid]"
|
||||||
:class="currentField.type === 'signature' ? 'mt-2' : 'mt-6 md:mt-8'"
|
:class="currentField.type === 'signature' ? 'mt-2' : 'mt-4 md:mt-6'"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
id="submit_form_button"
|
id="submit_form_button"
|
||||||
ref="submitButton"
|
ref="submitButton"
|
||||||
type="submit"
|
type="submit"
|
||||||
class="base-button w-full flex justify-center"
|
class="base-button w-full flex justify-center submit-form-button"
|
||||||
:disabled="isButtonDisabled"
|
:disabled="isButtonDisabled"
|
||||||
>
|
>
|
||||||
<span class="flex">
|
<span class="flex">
|
||||||
@@ -522,15 +531,15 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="stepFields.length < 80"
|
v-if="stepFields.length < 80"
|
||||||
class="flex justify-center"
|
class="flex justify-center mt-3 sm:mt-4 mb-0 sm:mb-1"
|
||||||
>
|
>
|
||||||
<div class="flex items-center mt-4 mb-1 flex-wrap">
|
<div class="flex items-center flex-wrap steps-progress">
|
||||||
<a
|
<a
|
||||||
v-for="(step, index) in stepFields"
|
v-for="(step, index) in stepFields"
|
||||||
:key="step[0].uuid"
|
:key="step[0].uuid"
|
||||||
href="#"
|
href="#"
|
||||||
class="inline border border-base-300 h-3 w-3 rounded-full mx-1 mt-1"
|
class="inline border border-base-300 h-3 w-3 rounded-full mx-1 mt-1"
|
||||||
:class="{ 'bg-base-300': index === currentStep, 'bg-base-content': (index < currentStep && stepFields[index].every((f) => !f.required || ![null, undefined, ''].includes(values[f.uuid]))) || isCompleted, 'bg-white': index > currentStep }"
|
:class="{ 'bg-base-300 steps-progress-current': index === currentStep, 'bg-base-content': (index < currentStep && stepFields[index].every((f) => !f.required || ![null, undefined, ''].includes(values[f.uuid]))) || isCompleted, 'bg-white': index > currentStep }"
|
||||||
@click.prevent="isCompleted ? '' : [saveStep(), goToStep(index, true)]"
|
@click.prevent="isCompleted ? '' : [saveStep(), goToStep(index, true)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1028,7 +1037,7 @@ export default {
|
|||||||
}, [])
|
}, [])
|
||||||
},
|
},
|
||||||
formulaFields () {
|
formulaFields () {
|
||||||
return this.fields.filter((f) => f.preferences?.formula && f.type !== 'payment')
|
return this.fields.filter((f) => f.preferences?.formula && f.type !== 'payment' && this.checkFieldConditions(f) && this.checkFieldDocumentsConditions(f))
|
||||||
},
|
},
|
||||||
attachmentsIndex () {
|
attachmentsIndex () {
|
||||||
return this.attachments.reduce((acc, a) => {
|
return this.attachments.reduce((acc, a) => {
|
||||||
@@ -1182,22 +1191,24 @@ export default {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultValue = !field || isEmpty(field.default_value) ? null : field.default_value
|
||||||
|
|
||||||
if (['empty', 'unchecked'].includes(condition.action)) {
|
if (['empty', 'unchecked'].includes(condition.action)) {
|
||||||
return isEmpty(this.values[condition.field_uuid])
|
return isEmpty(this.values[condition.field_uuid] ?? defaultValue)
|
||||||
} else if (['not_empty', 'checked'].includes(condition.action)) {
|
} else if (['not_empty', 'checked'].includes(condition.action)) {
|
||||||
return !isEmpty(this.values[condition.field_uuid])
|
return !isEmpty(this.values[condition.field_uuid] ?? defaultValue)
|
||||||
} else if (['equal', 'contains'].includes(condition.action) && field) {
|
} else if (['equal', 'contains'].includes(condition.action) && field) {
|
||||||
if (field.options) {
|
if (field.options) {
|
||||||
const option = field.options.find((o) => o.uuid === condition.value)
|
const option = field.options.find((o) => o.uuid === condition.value)
|
||||||
const values = [this.values[condition.field_uuid]].flat()
|
const values = [this.values[condition.field_uuid] ?? defaultValue].flat()
|
||||||
|
|
||||||
return values.includes(this.optionValue(option, field.options.indexOf(option)))
|
return values.includes(this.optionValue(option, field.options.indexOf(option)))
|
||||||
} else {
|
} else {
|
||||||
return [this.values[condition.field_uuid]].flat().includes(condition.value)
|
return [this.values[condition.field_uuid] ?? defaultValue].flat().includes(condition.value)
|
||||||
}
|
}
|
||||||
} else if (['not_equal', 'does_not_contain'].includes(condition.action) && field) {
|
} else if (['not_equal', 'does_not_contain'].includes(condition.action) && field) {
|
||||||
const option = field.options.find((o) => o.uuid === condition.value)
|
const option = field.options.find((o) => o.uuid === condition.value)
|
||||||
const values = [this.values[condition.field_uuid]].flat()
|
const values = [this.values[condition.field_uuid] ?? defaultValue].flat()
|
||||||
|
|
||||||
return !values.includes(this.optionValue(option, field.options.indexOf(option)))
|
return !values.includes(this.optionValue(option, field.options.indexOf(option)))
|
||||||
} else {
|
} else {
|
||||||
@@ -1290,7 +1301,9 @@ export default {
|
|||||||
|
|
||||||
if (!this.isCompleted) {
|
if (!this.isCompleted) {
|
||||||
if (scrollToArea) {
|
if (scrollToArea) {
|
||||||
this.scrollIntoField(this.currentField)
|
this.$nextTick(() => {
|
||||||
|
setTimeout(() => this.scrollIntoField(this.currentField), 1)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.enableScrollIntoField = false
|
this.enableScrollIntoField = false
|
||||||
@@ -1307,7 +1320,7 @@ export default {
|
|||||||
const currentFieldUuids = this.currentStepFields.map((f) => f.uuid)
|
const currentFieldUuids = this.currentStepFields.map((f) => f.uuid)
|
||||||
const currentFieldType = this.currentField.type
|
const currentFieldType = this.currentField.type
|
||||||
|
|
||||||
if (!formData && !this.$refs.form.checkValidity()) {
|
if (!formData && !this.$refs.form.checkValidity() && currentFieldUuids.every((fieldUuid) => isEmpty(this.submittedValues[fieldUuid]) || !isEmpty(this.values[fieldUuid]))) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="modelValue">
|
<div v-if="modelValue">
|
||||||
<div class="flex justify-between items-center w-full mb-2">
|
<div class="flex justify-between items-end w-full mb-3.5 md:mb-4">
|
||||||
<label
|
<label
|
||||||
class="label text-2xl"
|
v-if="showFieldNames"
|
||||||
>{{ showFieldNames && field.name ? field.name : t('image') }}</label>
|
:for="field.uuid"
|
||||||
|
class="label text-xl sm:text-2xl py-0 field-name-label"
|
||||||
|
>
|
||||||
|
<MarkdownContent
|
||||||
|
v-if="field.title"
|
||||||
|
:string="field.title"
|
||||||
|
/>
|
||||||
|
<template v-else>
|
||||||
|
{{ field.name || t('image') }}
|
||||||
|
</template>
|
||||||
|
</label>
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline btn-sm"
|
class="btn btn-outline btn-sm reupload-button"
|
||||||
@click.prevent="remove"
|
@click.prevent="remove"
|
||||||
>
|
>
|
||||||
<IconReload :width="16" />
|
<IconReload :width="16" />
|
||||||
@@ -15,7 +25,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<img
|
<img
|
||||||
:src="attachmentsIndex[modelValue].url"
|
:src="attachmentsIndex[modelValue].url"
|
||||||
class="h-52 border border-base-300 rounded mx-auto"
|
class="h-52 border border-base-300 rounded mx-auto uploaded-image-preview"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
@@ -30,12 +40,12 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
<FileDropzone
|
<FileDropzone
|
||||||
:message="`${t('upload')} ${field.name || t('image')}${field.required ? '' : ` (${t('optional')})`}`"
|
:message="`${t('upload')} ${(field.title || field.name) || t('image')}${field.required ? '' : ` (${t('optional')})`}`"
|
||||||
:submitter-slug="submitterSlug"
|
:submitter-slug="submitterSlug"
|
||||||
:dry-run="dryRun"
|
:dry-run="dryRun"
|
||||||
:accept="'image/*'"
|
:accept="'image/*'"
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div dir="auto">
|
<div dir="auto">
|
||||||
<div
|
<div
|
||||||
class="flex justify-between items-center w-full"
|
class="flex justify-between items-end w-full mb-3.5 md:mb-4"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="label text-2xl"
|
v-if="showFieldNames"
|
||||||
|
class="label text-xl sm:text-2xl py-0 field-name-label"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
v-if="field.title"
|
v-if="field.title"
|
||||||
:string="field.title"
|
:string="field.title"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ showFieldNames && field.name ? field.name : t('initials') }}
|
{{ field.name || t('initials') }}
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div class="space-x-2 flex">
|
<div class="space-x-2 flex flex-none">
|
||||||
<span
|
<span
|
||||||
v-if="isDrawInitials"
|
v-if="isDrawInitials"
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
<a
|
<a
|
||||||
id="type_text_button"
|
id="type_text_button"
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-outline font-medium btn-sm"
|
class="btn btn-outline font-medium btn-sm type-text-button"
|
||||||
@click.prevent="toggleTextInput"
|
@click.prevent="toggleTextInput"
|
||||||
>
|
>
|
||||||
<IconTextSize :width="16" />
|
<IconTextSize :width="16" />
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
<a
|
<a
|
||||||
id="type_text_button"
|
id="type_text_button"
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-outline font-medium btn-sm"
|
class="btn btn-outline font-medium btn-sm type-text-button"
|
||||||
@click.prevent="toggleTextInput"
|
@click.prevent="toggleTextInput"
|
||||||
>
|
>
|
||||||
<IconSignature :width="16" />
|
<IconSignature :width="16" />
|
||||||
@@ -54,9 +55,7 @@
|
|||||||
class="tooltip"
|
class="tooltip"
|
||||||
:data-tip="t('click_to_upload')"
|
:data-tip="t('click_to_upload')"
|
||||||
>
|
>
|
||||||
<label
|
<label class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap upload-image-button">
|
||||||
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap"
|
|
||||||
>
|
|
||||||
<IconUpload :width="16" />
|
<IconUpload :width="16" />
|
||||||
<input
|
<input
|
||||||
:key="uploadImageInputKey"
|
:key="uploadImageInputKey"
|
||||||
@@ -73,7 +72,7 @@
|
|||||||
<a
|
<a
|
||||||
v-if="modelValue || computedPreviousValue"
|
v-if="modelValue || computedPreviousValue"
|
||||||
href="#"
|
href="#"
|
||||||
class="btn font-medium btn-outline btn-sm"
|
class="btn font-medium btn-outline btn-sm clear-canvas-button"
|
||||||
@click.prevent="remove"
|
@click.prevent="remove"
|
||||||
>
|
>
|
||||||
<IconReload :width="16" />
|
<IconReload :width="16" />
|
||||||
@@ -82,7 +81,7 @@
|
|||||||
<a
|
<a
|
||||||
v-else
|
v-else
|
||||||
href="#"
|
href="#"
|
||||||
class="btn font-medium btn-outline btn-sm"
|
class="btn font-medium btn-outline btn-sm clear-canvas-button"
|
||||||
@click.prevent="clear"
|
@click.prevent="clear"
|
||||||
>
|
>
|
||||||
<IconReload :width="16" />
|
<IconReload :width="16" />
|
||||||
@@ -104,7 +103,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -122,7 +121,7 @@
|
|||||||
<canvas
|
<canvas
|
||||||
v-show="!modelValue && !computedPreviousValue"
|
v-show="!modelValue && !computedPreviousValue"
|
||||||
ref="canvas"
|
ref="canvas"
|
||||||
class="bg-white border border-base-300 rounded-2xl w-full"
|
class="bg-white border border-base-300 rounded-2xl w-full draw-canvas"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-if="!isDrawInitials && !modelValue && !computedPreviousValue"
|
v-if="!isDrawInitials && !modelValue && !computedPreviousValue"
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mt-6 md:mt-8"
|
class="mt-4 md:mt-6"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -3,18 +3,24 @@
|
|||||||
v-if="showFieldNames && (field.name || field.title)"
|
v-if="showFieldNames && (field.name || field.title)"
|
||||||
:for="field.uuid"
|
:for="field.uuid"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
><MarkdownContent
|
>
|
||||||
v-if="field.title"
|
<MarkdownContent
|
||||||
:string="field.title"
|
v-if="field.title"
|
||||||
/>
|
:string="field.title"
|
||||||
|
/>
|
||||||
<template v-else>{{ field.name }}</template>
|
<template v-else>{{ field.name }}</template>
|
||||||
|
<template v-if="!field.required">
|
||||||
|
<span :class="{ 'hidden sm:inline': (field.title || field.name).length > 20 }">
|
||||||
|
({{ t('optional') }})
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -44,7 +50,7 @@
|
|||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
:for="option.uuid"
|
:for="option.uuid"
|
||||||
class="flex items-center space-x-3"
|
class="flex items-center space-x-3 checkbox-label"
|
||||||
@click="scrollIntoField(field)"
|
@click="scrollIntoField(field)"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -3,15 +3,18 @@
|
|||||||
v-if="showFieldNames && (field.name || field.title)"
|
v-if="showFieldNames && (field.name || field.title)"
|
||||||
:for="field.uuid"
|
:for="field.uuid"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
><MarkdownContent
|
>
|
||||||
v-if="field.title"
|
<MarkdownContent
|
||||||
:string="field.title"
|
v-if="field.title"
|
||||||
/>
|
:string="field.title"
|
||||||
|
/>
|
||||||
<template v-else>{{ field.name }}</template>
|
<template v-else>{{ field.name }}</template>
|
||||||
<template v-if="!field.required">
|
<template v-if="!field.required">
|
||||||
({{ t('optional') }})
|
<span :class="{ 'hidden sm:inline': (field.title || field.name).length > 20 }">
|
||||||
|
({{ t('optional') }})
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
@@ -21,7 +24,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
v-if="!modelValue && !sessionId"
|
v-if="!modelValue && !sessionId"
|
||||||
class="label text-2xl mb-2"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
v-if="field.title"
|
v-if="field.title"
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="sessionId"
|
v-if="sessionId"
|
||||||
disabled
|
disabled
|
||||||
class="base-button w-full"
|
class="base-button w-full modal-save-button"
|
||||||
>
|
>
|
||||||
<IconLoader
|
<IconLoader
|
||||||
width="22"
|
width="22"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
|
v-if="showFieldNames"
|
||||||
:for="isCodeSent ? 'one_time_code' : field.uuid"
|
:for="isCodeSent ? 'one_time_code' : field.uuid"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
@@ -10,16 +11,13 @@
|
|||||||
:string="field.title"
|
:string="field.title"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ showFieldNames && field.name ? field.name : t('verified_phone_number') }}
|
{{ field.name || t('verified_phone_number') }}
|
||||||
<template v-if="!field.required">
|
|
||||||
({{ t('optional') }})
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -47,7 +45,7 @@
|
|||||||
<a
|
<a
|
||||||
v-if="!defaultValue"
|
v-if="!defaultValue"
|
||||||
href="#"
|
href="#"
|
||||||
class="link"
|
class="link change-phone-number-link"
|
||||||
@click.prevent="isCodeSent = false"
|
@click.prevent="isCodeSent = false"
|
||||||
>
|
>
|
||||||
{{ t('change_phone_number') }}
|
{{ t('change_phone_number') }}
|
||||||
@@ -61,7 +59,7 @@
|
|||||||
<a
|
<a
|
||||||
v-else
|
v-else
|
||||||
href="#"
|
href="#"
|
||||||
class="link"
|
class="link resend-code-link"
|
||||||
@click.prevent="resendCode"
|
@click.prevent="resendCode"
|
||||||
>
|
>
|
||||||
{{ isResendLoading ? t('sending') : t('resend_code') }}
|
{{ isResendLoading ? t('sending') : t('resend_code') }}
|
||||||
@@ -70,13 +68,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-show="!isCodeSent"
|
v-show="!isCodeSent"
|
||||||
class="flex w-full rounded-full outline-neutral-content outline-2 outline-offset-2 focus-within:outline"
|
class="flex w-full rounded-full outline-neutral-content outline-2 outline-offset-2 focus-within:outline phone-number-input-container"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="country_code"
|
id="country_code"
|
||||||
class="relative inline-block"
|
class="relative inline-block"
|
||||||
>
|
>
|
||||||
<div class="btn bg-base-200 border border-neutral-300 text-2xl whitespace-nowrap font-normal rounded-l-full">
|
<div class="btn bg-base-200 border border-neutral-300 text-2xl whitespace-nowrap font-normal rounded-l-full country-code-select-label">
|
||||||
{{ selectedCountry.flag }} +{{ selectedCountry.dial }}
|
{{ selectedCountry.flag }} +{{ selectedCountry.dial }}
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -4,21 +4,22 @@
|
|||||||
class="relative"
|
class="relative"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex justify-between items-center w-full"
|
class="flex justify-between items-end w-full mb-3.5 md:mb-4"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="label text-2xl"
|
v-if="showFieldNames"
|
||||||
|
class="label text-xl sm:text-2xl py-0 field-name-label"
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
v-if="field.title"
|
v-if="field.title"
|
||||||
:string="field.title"
|
:string="field.title"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ showFieldNames && field.name ? field.name : t('signature') }}
|
{{ field.name || t('signature') }}
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div class="space-x-2 flex">
|
<div class="space-x-2 flex flex-none">
|
||||||
<span
|
<span
|
||||||
v-if="isTextSignature && format !== 'typed' && format !== 'upload'"
|
v-if="isTextSignature && format !== 'typed' && format !== 'upload'"
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
@@ -27,7 +28,7 @@
|
|||||||
<a
|
<a
|
||||||
id="type_text_button"
|
id="type_text_button"
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-outline btn-sm font-medium"
|
class="btn btn-outline btn-sm font-medium type-text-button"
|
||||||
@click.prevent="[toggleTextInput(), hideQr()]"
|
@click.prevent="[toggleTextInput(), hideQr()]"
|
||||||
>
|
>
|
||||||
<IconSignature :width="16" />
|
<IconSignature :width="16" />
|
||||||
@@ -39,12 +40,13 @@
|
|||||||
<span
|
<span
|
||||||
v-else-if="withTypedSignature && format !== 'typed' && format !== 'drawn' && format !== 'upload'"
|
v-else-if="withTypedSignature && format !== 'typed' && format !== 'drawn' && format !== 'upload'"
|
||||||
class="tooltip ml-2"
|
class="tooltip ml-2"
|
||||||
|
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue }"
|
||||||
:data-tip="t('type_text')"
|
:data-tip="t('type_text')"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
id="type_text_button"
|
id="type_text_button"
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap"
|
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap type-text-button"
|
||||||
@click.prevent="[toggleTextInput(), hideQr()]"
|
@click.prevent="[toggleTextInput(), hideQr()]"
|
||||||
>
|
>
|
||||||
<IconTextSize :width="16" />
|
<IconTextSize :width="16" />
|
||||||
@@ -56,11 +58,10 @@
|
|||||||
<span
|
<span
|
||||||
v-if="format !== 'typed' && format !== 'drawn' && format !== 'upload' && format !== 'drawn_or_typed'"
|
v-if="format !== 'typed' && format !== 'drawn' && format !== 'upload' && format !== 'drawn_or_typed'"
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
|
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue }"
|
||||||
:data-tip="t('take_photo')"
|
:data-tip="t('take_photo')"
|
||||||
>
|
>
|
||||||
<label
|
<label class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap upload-image-button">
|
||||||
class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap"
|
|
||||||
>
|
|
||||||
<IconCamera :width="16" />
|
<IconCamera :width="16" />
|
||||||
<input
|
<input
|
||||||
:key="uploadImageInputKey"
|
:key="uploadImageInputKey"
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
<a
|
<a
|
||||||
v-if="modelValue || computedPreviousValue"
|
v-if="modelValue || computedPreviousValue"
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-outline btn-sm font-medium"
|
class="btn btn-outline btn-sm font-medium reupload-button"
|
||||||
@click.prevent="remove"
|
@click.prevent="remove"
|
||||||
>
|
>
|
||||||
<IconReload :width="16" />
|
<IconReload :width="16" />
|
||||||
@@ -116,7 +117,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -164,7 +165,7 @@
|
|||||||
v-show="!modelValue && !computedPreviousValue"
|
v-show="!modelValue && !computedPreviousValue"
|
||||||
ref="canvas"
|
ref="canvas"
|
||||||
style="padding: 1px; 0"
|
style="padding: 1px; 0"
|
||||||
class="bg-white border border-base-300 rounded-2xl w-full"
|
class="bg-white border border-base-300 rounded-2xl w-full draw-canvas"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="isShowQr"
|
v-if="isShowQr"
|
||||||
|
|||||||
@@ -3,15 +3,18 @@
|
|||||||
v-if="showFieldNames && (field.name || field.title)"
|
v-if="showFieldNames && (field.name || field.title)"
|
||||||
:for="field.uuid"
|
:for="field.uuid"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="label text-2xl"
|
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
|
||||||
:class="{ 'mb-2': !field.description }"
|
:class="{ 'mb-2': !field.description }"
|
||||||
><MarkdownContent
|
>
|
||||||
v-if="field.title"
|
<MarkdownContent
|
||||||
:string="field.title"
|
v-if="field.title"
|
||||||
/>
|
:string="field.title"
|
||||||
|
/>
|
||||||
<template v-else>{{ field.name }}</template>
|
<template v-else>{{ field.name }}</template>
|
||||||
<template v-if="!field.required">
|
<template v-if="!field.required">
|
||||||
({{ t('optional') }})
|
<span :class="{ 'hidden sm:inline': (field.title || field.name).length > 20 }">
|
||||||
|
({{ t('optional') }})
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
@@ -21,7 +24,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-3 px-1"
|
class="mb-3 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
@@ -64,7 +67,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-ghost btn-circle btn-sm"
|
class="btn btn-ghost btn-circle btn-sm toggle-multiline-text-button"
|
||||||
@click.prevent="toggleTextArea"
|
@click.prevent="toggleTextArea"
|
||||||
>
|
>
|
||||||
<IconAlignBoxLeftTop />
|
<IconAlignBoxLeftTop />
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<label
|
<label class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label">
|
||||||
class="label text-2xl mb-2"
|
|
||||||
>
|
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
v-if="field.title"
|
v-if="field.title"
|
||||||
:string="field.title"
|
:string="field.title"
|
||||||
@@ -11,13 +9,13 @@
|
|||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
class="mb-4 px-1"
|
class="mb-4 px-1 field-description-text"
|
||||||
>
|
>
|
||||||
<MarkdownContent :string="field.description" />
|
<MarkdownContent :string="field.description" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="emptyValueRequiredStep && emptyValueRequiredStep[0] !== field"
|
v-if="emptyValueRequiredStep && emptyValueRequiredStep[0] !== field"
|
||||||
class="px-1"
|
class="px-1 field-description-text"
|
||||||
>
|
>
|
||||||
{{ t('complete_all_required_fields_to_proceed_with_identity_verification') }}
|
{{ t('complete_all_required_fields_to_proceed_with_identity_verification') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -31,6 +29,15 @@
|
|||||||
class="animate-spin h-10"
|
class="animate-spin h-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="redirectUrl">
|
||||||
|
<a
|
||||||
|
:href="redirectUrl"
|
||||||
|
target="_blank"
|
||||||
|
class="white-button w-full"
|
||||||
|
>
|
||||||
|
{{ t('verify_id') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
ref="widgetContainer"
|
ref="widgetContainer"
|
||||||
/>
|
/>
|
||||||
@@ -87,6 +94,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
isCreatingCheckout: false,
|
isCreatingCheckout: false,
|
||||||
isMathLoaded: false,
|
isMathLoaded: false,
|
||||||
|
redirectUrl: '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
eidEasyData: {}
|
eidEasyData: {}
|
||||||
}
|
}
|
||||||
@@ -147,14 +155,26 @@ export default {
|
|||||||
}).then(async (resp) => {
|
}).then(async (resp) => {
|
||||||
this.eidEasyData = await resp.json()
|
this.eidEasyData = await resp.json()
|
||||||
|
|
||||||
const eidEasyWidget = document.createElement('eideasy-widget')
|
if (this.eidEasyData.available_methods[0] === 'itsme-qes-signature' &&
|
||||||
|
this.eidEasyData.available_methods.length === 1) {
|
||||||
|
const redirectUrl = new URL('https://id.eideasy.com/sign_contract_external')
|
||||||
|
|
||||||
for (const key in this.widgetSettings) {
|
redirectUrl.searchParams.append('client_id', this.eidEasyData.client_id)
|
||||||
eidEasyWidget[key] = this.widgetSettings[key]
|
redirectUrl.searchParams.append('doc_id', this.eidEasyData.doc_id)
|
||||||
|
redirectUrl.searchParams.append('country', this.countryCode)
|
||||||
|
redirectUrl.searchParams.append('lang', this.locale)
|
||||||
|
|
||||||
|
this.redirectUrl = redirectUrl.toString()
|
||||||
|
} else {
|
||||||
|
const eidEasyWidget = document.createElement('eideasy-widget')
|
||||||
|
|
||||||
|
for (const key in this.widgetSettings) {
|
||||||
|
eidEasyWidget[key] = this.widgetSettings[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.widgetContainer.innerHTML = ''
|
||||||
|
this.$refs.widgetContainer.appendChild(eidEasyWidget)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$refs.widgetContainer.innerHTML = ''
|
|
||||||
this.$refs.widgetContainer.appendChild(eidEasyWidget)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async submit () {
|
async submit () {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="absolute overflow-visible group"
|
class="absolute overflow-visible group field-area-container"
|
||||||
:style="positionStyle"
|
:style="positionStyle"
|
||||||
:class="{ 'z-[1]': isMoved || isDragged }"
|
:class="{ 'z-[1]': isMoved || isDragged }"
|
||||||
@pointerdown.stop
|
@pointerdown.stop
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="field?.type && (isSelected || isNameFocus)"
|
v-if="field?.type && (isSelected || isNameFocus)"
|
||||||
class="absolute bg-white rounded-t border overflow-visible whitespace-nowrap flex z-10"
|
class="absolute bg-white rounded-t border overflow-visible whitespace-nowrap flex z-10 field-area-controls"
|
||||||
style="top: -25px; height: 25px"
|
style="top: -25px; height: 25px"
|
||||||
@mousedown.stop
|
@mousedown.stop
|
||||||
@pointerdown.stop
|
@pointerdown.stop
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<FieldSubmitter
|
<FieldSubmitter
|
||||||
v-if="field.type != 'heading'"
|
v-if="field.type != 'heading'"
|
||||||
v-model="field.submitter_uuid"
|
v-model="field.submitter_uuid"
|
||||||
class="border-r"
|
class="border-r roles-dropdown"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
:editable="editable && (!defaultField || defaultField.role !== submitter?.name)"
|
:editable="editable && (!defaultField || defaultField.role !== submitter?.name)"
|
||||||
:allow-add-new="!defaultSubmitters.length"
|
:allow-add-new="!defaultSubmitters.length"
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
>{{ t('editable') }}</label>
|
>{{ t('editable') }}</label>
|
||||||
<span
|
<span
|
||||||
v-if="field.type !== 'payment' && !isValueInput"
|
v-if="field.type !== 'payment' && !isValueInput"
|
||||||
class="dropdown dropdown-end"
|
class="dropdown dropdown-end field-area-settings-dropdown"
|
||||||
@mouseenter="renderDropdown = true"
|
@mouseenter="renderDropdown = true"
|
||||||
@touchstart="renderDropdown = true"
|
@touchstart="renderDropdown = true"
|
||||||
>
|
>
|
||||||
@@ -141,6 +141,7 @@
|
|||||||
:with-required="false"
|
:with-required="false"
|
||||||
:with-areas="false"
|
:with-areas="false"
|
||||||
@click-formula="isShowFormulaModal = true"
|
@click-formula="isShowFormulaModal = true"
|
||||||
|
@click-font="isShowFontModal = true"
|
||||||
@click-description="isShowDescriptionModal = true"
|
@click-description="isShowDescriptionModal = true"
|
||||||
@click-condition="isShowConditionsModal = true"
|
@click-condition="isShowConditionsModal = true"
|
||||||
@scroll-to="[selectedAreaRef.value = $event, $emit('scroll-to', $event)]"
|
@scroll-to="[selectedAreaRef.value = $event, $emit('scroll-to', $event)]"
|
||||||
@@ -159,9 +160,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref="touchValueTarget"
|
ref="touchValueTarget"
|
||||||
class="flex items-center h-full w-full"
|
class="flex items-center h-full w-full field-area"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
:class="[isValueInput ? 'bg-opacity-50' : 'bg-opacity-80', field.type === 'heading' ? 'bg-gray-50' : bgColors[submitterIndex % bgColors.length], isDefaultValuePresent || isValueInput || (withFieldPlaceholder && field.areas) ? (alignClasses[field.preferences?.align] || '') : 'justify-center']"
|
:class="[isValueInput ? 'bg-opacity-50' : 'bg-opacity-80', field.type === 'heading' ? 'bg-gray-50' : bgColors[submitterIndex % bgColors.length], isDefaultValuePresent || isValueInput || (withFieldPlaceholder && field.areas) ? fontClasses : 'justify-center']"
|
||||||
@click="focusValueInput"
|
@click="focusValueInput"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -253,6 +254,17 @@
|
|||||||
@close="isShowFormulaModal = false"
|
@close="isShowFormulaModal = false"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
<Teleport
|
||||||
|
v-if="isShowFontModal"
|
||||||
|
:to="modalContainerEl"
|
||||||
|
>
|
||||||
|
<FontModal
|
||||||
|
:field="field"
|
||||||
|
:editable="editable && !defaultField"
|
||||||
|
:build-default-name="buildDefaultName"
|
||||||
|
@close="isShowFontModal = false"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
<Teleport
|
<Teleport
|
||||||
v-if="isShowConditionsModal"
|
v-if="isShowConditionsModal"
|
||||||
:to="modalContainerEl"
|
:to="modalContainerEl"
|
||||||
@@ -283,6 +295,7 @@ import FieldType from './field_type'
|
|||||||
import Field from './field'
|
import Field from './field'
|
||||||
import FieldSettings from './field_settings'
|
import FieldSettings from './field_settings'
|
||||||
import FormulaModal from './formula_modal'
|
import FormulaModal from './formula_modal'
|
||||||
|
import FontModal from './font_modal'
|
||||||
import ConditionsModal from './conditions_modal'
|
import ConditionsModal from './conditions_modal'
|
||||||
import DescriptionModal from './description_modal'
|
import DescriptionModal from './description_modal'
|
||||||
import { IconX, IconCheck, IconDotsVertical } from '@tabler/icons-vue'
|
import { IconX, IconCheck, IconDotsVertical } from '@tabler/icons-vue'
|
||||||
@@ -295,6 +308,7 @@ export default {
|
|||||||
IconCheck,
|
IconCheck,
|
||||||
FieldSettings,
|
FieldSettings,
|
||||||
FormulaModal,
|
FormulaModal,
|
||||||
|
FontModal,
|
||||||
IconDotsVertical,
|
IconDotsVertical,
|
||||||
DescriptionModal,
|
DescriptionModal,
|
||||||
ConditionsModal,
|
ConditionsModal,
|
||||||
@@ -352,6 +366,7 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isShowFormulaModal: false,
|
isShowFormulaModal: false,
|
||||||
|
isShowFontModal: false,
|
||||||
isShowConditionsModal: false,
|
isShowConditionsModal: false,
|
||||||
isContenteditable: false,
|
isContenteditable: false,
|
||||||
isSettingsFocus: false,
|
isSettingsFocus: false,
|
||||||
@@ -386,11 +401,19 @@ export default {
|
|||||||
defaultName () {
|
defaultName () {
|
||||||
return this.buildDefaultName(this.field, this.template.fields)
|
return this.buildDefaultName(this.field, this.template.fields)
|
||||||
},
|
},
|
||||||
alignClasses () {
|
fontClasses () {
|
||||||
|
if (!this.field.preferences) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
center: 'justify-center',
|
'justify-center': this.field.preferences.align === 'center',
|
||||||
left: 'justify-start',
|
'justify-start': this.field.preferences.align === 'left',
|
||||||
right: 'justify-end'
|
'justify-end': this.field.preferences.align === 'right',
|
||||||
|
'font-mono': this.field.preferences.font === 'Courier',
|
||||||
|
'font-serif': this.field.preferences.font === 'Times',
|
||||||
|
'font-bold': ['bold_italic', 'bold'].includes(this.field.preferences.font_type),
|
||||||
|
italic: ['bold_italic', 'italic'].includes(this.field.preferences.font_type)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
optionIndexText () {
|
optionIndexText () {
|
||||||
@@ -483,6 +506,10 @@ export default {
|
|||||||
this.$el.getRootNode().activeElement.blur()
|
this.$el.getRootNode().activeElement.blur()
|
||||||
},
|
},
|
||||||
maybeToggleDefaultValue () {
|
maybeToggleDefaultValue () {
|
||||||
|
if (!this.editable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (['text', 'number'].includes(this.field.type)) {
|
if (['text', 'number'].includes(this.field.type)) {
|
||||||
this.isContenteditable = true
|
this.isContenteditable = true
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="$slots.buttons || withTitle"
|
v-if="$slots.buttons || withTitle"
|
||||||
id="title_container"
|
id="title_container"
|
||||||
class="flex justify-between py-1.5 items-center pr-4 top-0 z-10"
|
class="flex justify-between py-1.5 items-center pr-4 top-0 z-10 title-container"
|
||||||
:class="{ sticky: withStickySubmitters || isBreakpointLg }"
|
:class="{ sticky: withStickySubmitters || isBreakpointLg }"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
>
|
>
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
v-if="withTitle"
|
v-if="withTitle"
|
||||||
:model-value="template.name"
|
:model-value="template.name"
|
||||||
:editable="editable"
|
:editable="editable"
|
||||||
class="text-xl md:text-3xl font-semibold focus:text-clip"
|
class="text-xl md:text-3xl font-semibold focus:text-clip template-name"
|
||||||
:icon-stroke-width="2.3"
|
:icon-stroke-width="2.3"
|
||||||
@update:model-value="updateName"
|
@update:model-value="updateName"
|
||||||
/>
|
/>
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="main_container"
|
id="main_container"
|
||||||
class="flex"
|
class="flex main-container"
|
||||||
:class="$slots.buttons || withTitle ? (isMobile ? 'max-h-[calc(100%_-_60px)]' : 'md:max-h-[calc(100%_-_60px)]') : (isMobile ? 'max-h-[100%]' : 'md:max-h-[100%]')"
|
:class="$slots.buttons || withTitle ? (isMobile ? 'max-h-[calc(100%_-_60px)]' : 'md:max-h-[calc(100%_-_60px)]') : (isMobile ? 'max-h-[100%]' : 'md:max-h-[100%]')"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="sortedDocuments.length && editable && withAddPageButton"
|
v-if="sortedDocuments.length && editable && withAddPageButton"
|
||||||
id="add_blank_page_button"
|
id="add_blank_page_button"
|
||||||
class="btn btn-outline w-full"
|
class="btn btn-outline w-full add-blank-page-button"
|
||||||
@click.prevent="addBlankPage"
|
@click.prevent="addBlankPage"
|
||||||
>
|
>
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="withAddPageButton"
|
v-if="withAddPageButton"
|
||||||
id="add_blank_page_button"
|
id="add_blank_page_button"
|
||||||
class="btn btn-outline w-full mt-4"
|
class="btn btn-outline w-full mt-4 add-blank-page-button"
|
||||||
@click.prevent="addBlankPage"
|
@click.prevent="addBlankPage"
|
||||||
>
|
>
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
:is-drag="!!dragField"
|
:is-drag="!!dragField"
|
||||||
:input-mode="inputMode"
|
:input-mode="inputMode"
|
||||||
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
||||||
:allow-draw="!onlyDefinedFields"
|
:allow-draw="!onlyDefinedFields || drawField"
|
||||||
:data-document-uuid="document.uuid"
|
:data-document-uuid="document.uuid"
|
||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
:with-field-placeholder="withFieldPlaceholder"
|
:with-field-placeholder="withFieldPlaceholder"
|
||||||
@@ -307,7 +307,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="withAddPageButton"
|
v-if="withAddPageButton"
|
||||||
id="add_blank_page_button"
|
id="add_blank_page_button"
|
||||||
class="btn btn-outline w-full mt-4"
|
class="btn btn-outline w-full mt-4 add-blank-page-button"
|
||||||
@click.prevent="addBlankPage"
|
@click.prevent="addBlankPage"
|
||||||
>
|
>
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="withFieldsList && !isMobile"
|
v-if="withFieldsList && !isMobile"
|
||||||
id="fields_list_container"
|
id="fields_list_container"
|
||||||
class="relative w-80 flex-none mt-1 pr-4 pl-0.5 hidden md:block"
|
class="relative w-80 flex-none mt-1 pr-4 pl-0.5 hidden md:block fields-list-container"
|
||||||
:class="drawField ? 'overflow-hidden' : 'overflow-y-auto overflow-x-hidden'"
|
:class="drawField ? 'overflow-hidden' : 'overflow-y-auto overflow-x-hidden'"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -335,13 +335,13 @@
|
|||||||
class="sticky inset-0 h-full z-20"
|
class="sticky inset-0 h-full z-20"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
>
|
>
|
||||||
<div class="bg-base-200 rounded-lg p-5 text-center space-y-4">
|
<div class="bg-base-200 rounded-lg p-5 text-center space-y-4 draw-field-container">
|
||||||
<p>
|
<p>
|
||||||
{{ t('draw_field_on_the_document') }}
|
{{ t('draw_field_on_the_document') }}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="base-button"
|
class="base-button cancel-draw-button"
|
||||||
@click="clearDrawField"
|
@click="clearDrawField"
|
||||||
>
|
>
|
||||||
{{ t('cancel') }}
|
{{ t('cancel') }}
|
||||||
@@ -366,6 +366,7 @@
|
|||||||
:with-help="withHelp"
|
:with-help="withHelp"
|
||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
:draw-field-type="drawFieldType"
|
:draw-field-type="drawFieldType"
|
||||||
|
:with-fields-search="withFieldsSearch"
|
||||||
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
||||||
:template="template"
|
:template="template"
|
||||||
:default-required-fields="defaultRequiredFields"
|
:default-required-fields="defaultRequiredFields"
|
||||||
@@ -407,7 +408,10 @@
|
|||||||
@select="startFieldDraw($event)"
|
@select="startFieldDraw($event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div id="docuseal_modal_container" />
|
<div
|
||||||
|
id="docuseal_modal_container"
|
||||||
|
class="modal-container"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -623,6 +627,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
withFieldsSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
withFieldsList: {
|
withFieldsList: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -1310,6 +1319,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDropfield (area) {
|
onDropfield (area) {
|
||||||
|
if (!this.editable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const field = this.fieldsDragFieldRef.value || {
|
const field = this.fieldsDragFieldRef.value || {
|
||||||
name: '',
|
name: '',
|
||||||
uuid: v4(),
|
uuid: v4(),
|
||||||
@@ -1332,9 +1345,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === 'date') {
|
if (field.type === 'date') {
|
||||||
field.preferences = {
|
field.preferences ||= {}
|
||||||
format: this.defaultDateFormat
|
field.preferences.format ||= this.defaultDateFormat
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1395,7 +1407,6 @@ export default {
|
|||||||
const lastArea = field.areas[field.areas.length - 1]
|
const lastArea = field.areas[field.areas.length - 1]
|
||||||
|
|
||||||
if (lastArea) {
|
if (lastArea) {
|
||||||
fieldArea.x -= lastArea.w / 2
|
|
||||||
fieldArea.w = lastArea.w
|
fieldArea.w = lastArea.w
|
||||||
fieldArea.h = lastArea.h
|
fieldArea.h = lastArea.h
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
/>
|
/>
|
||||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||||
<span>
|
<span class="modal-title">
|
||||||
{{ t('condition') }} - {{ item.name || buildDefaultName(item, template.fields) }}
|
{{ t('condition') }} - {{ item.name || buildDefaultName(item, template.fields) }}
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="text-xl"
|
class="text-xl modal-close-button"
|
||||||
@click.prevent="$emit('close')"
|
@click.prevent="$emit('close')"
|
||||||
>×</a>
|
>×</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,9 +50,9 @@
|
|||||||
v-if="conditions.length > 1"
|
v-if="conditions.length > 1"
|
||||||
class="flex justify-between mx-1"
|
class="flex justify-between mx-1"
|
||||||
>
|
>
|
||||||
<span class="text-sm">
|
<label class="text-sm">
|
||||||
{{ t('condition') }} {{ cindex + 1 }}
|
{{ t('condition') }} {{ cindex + 1 }}
|
||||||
</span>
|
</label>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="link text-sm"
|
class="link text-sm"
|
||||||
@@ -131,9 +131,7 @@
|
|||||||
class="inline float-right link text-right mb-3 px-2"
|
class="inline float-right link text-right mb-3 px-2"
|
||||||
@click.prevent="conditions.push({})"
|
@click.prevent="conditions.push({})"
|
||||||
> + {{ t('add_condition') }}</a>
|
> + {{ t('add_condition') }}</a>
|
||||||
<button
|
<button class="base-button w-full mt-2 modal-save-button">
|
||||||
class="base-button w-full mt-2"
|
|
||||||
>
|
|
||||||
{{ t('save') }}
|
{{ t('save') }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -177,7 +175,7 @@ export default {
|
|||||||
fields () {
|
fields () {
|
||||||
if (this.item.submitter_uuid) {
|
if (this.item.submitter_uuid) {
|
||||||
return this.template.fields.reduce((acc, f) => {
|
return this.template.fields.reduce((acc, f) => {
|
||||||
if (f !== this.item && f.submitter_uuid === this.item.submitter_uuid) {
|
if (f !== this.item) {
|
||||||
acc.push(f)
|
acc.push(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<Contenteditable
|
<Contenteditable
|
||||||
class="w-full block mr-6"
|
class="w-full block mr-6 document-preview-name"
|
||||||
:model-value="item.name"
|
:model-value="item.name"
|
||||||
:icon-width="16"
|
:icon-width="16"
|
||||||
@update:model-value="onUpdateName"
|
@update:model-value="onUpdateName"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="withArrows"
|
v-if="withArrows"
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
||||||
style="width: 24px; height: 24px"
|
style="width: 24px; height: 24px"
|
||||||
@click.stop="$emit('up', item)"
|
@click.stop="$emit('up', item)"
|
||||||
>
|
>
|
||||||
@@ -23,14 +23,14 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="withArrows"
|
v-if="withArrows"
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
||||||
style="width: 24px; height: 24px"
|
style="width: 24px; height: 24px"
|
||||||
@click.stop="$emit('down', item)"
|
@click.stop="$emit('down', item)"
|
||||||
>
|
>
|
||||||
↓
|
↓
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
||||||
style="width: 24px; height: 24px"
|
style="width: 24px; height: 24px"
|
||||||
@click.stop="$emit('remove', item)"
|
@click.stop="$emit('remove', item)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
/>
|
/>
|
||||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||||
<span>
|
<span class="modal-title">
|
||||||
{{ field.name || buildDefaultName(field, template.fields) }}
|
{{ field.name || buildDefaultName(field, template.fields) }}
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="text-xl"
|
class="text-xl modal-close-button"
|
||||||
@click.prevent="$emit('close')"
|
@click.prevent="$emit('close')"
|
||||||
>×</a>
|
>×</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,9 +55,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button class="base-button w-full mt-4 modal-save-button">
|
||||||
class="base-button w-full mt-4"
|
|
||||||
>
|
|
||||||
{{ t('save') }}
|
{{ t('save') }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
class="list-field group mb-2"
|
class="list-field group mb-2"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="border border-base-300 rounded rounded-tr-none relative group"
|
class="border border-base-300 rounded rounded-tr-none relative group fields-list-item"
|
||||||
:style="{ backgroundColor: backgroundColor }"
|
:style="{ backgroundColor: backgroundColor }"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between relative group/contenteditable-container">
|
<div class="flex items-center justify-between relative group/contenteditable-container">
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-else-if="field.type !== 'heading'"
|
v-else-if="field.type !== 'heading'"
|
||||||
class="dropdown dropdown-end"
|
class="dropdown dropdown-end field-settings-dropdown"
|
||||||
@mouseenter="renderDropdown = true"
|
@mouseenter="renderDropdown = true"
|
||||||
@touchstart="renderDropdown = true"
|
@touchstart="renderDropdown = true"
|
||||||
>
|
>
|
||||||
@@ -127,6 +127,7 @@
|
|||||||
:editable="editable"
|
:editable="editable"
|
||||||
:background-color="dropdownBgColor"
|
:background-color="dropdownBgColor"
|
||||||
@click-formula="isShowFormulaModal = true"
|
@click-formula="isShowFormulaModal = true"
|
||||||
|
@click-font="isShowFontModal = true"
|
||||||
@click-description="isShowDescriptionModal = true"
|
@click-description="isShowDescriptionModal = true"
|
||||||
@click-condition="isShowConditionsModal = true"
|
@click-condition="isShowConditionsModal = true"
|
||||||
@set-draw="$emit('set-draw', $event)"
|
@set-draw="$emit('set-draw', $event)"
|
||||||
@@ -136,7 +137,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
class="relative text-transparent group-hover:text-base-content pr-1"
|
class="relative text-transparent group-hover:text-base-content pr-1 field-remove-button"
|
||||||
:title="t('remove')"
|
:title="t('remove')"
|
||||||
@click="$emit('remove', field)"
|
@click="$emit('remove', field)"
|
||||||
>
|
>
|
||||||
@@ -231,6 +232,17 @@
|
|||||||
@close="isShowFormulaModal = false"
|
@close="isShowFormulaModal = false"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
<Teleport
|
||||||
|
v-if="isShowFontModal"
|
||||||
|
:to="modalContainerEl"
|
||||||
|
>
|
||||||
|
<FontModal
|
||||||
|
:field="field"
|
||||||
|
:editable="editable && !defaultField"
|
||||||
|
:build-default-name="buildDefaultName"
|
||||||
|
@close="isShowFontModal = false"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
<Teleport
|
<Teleport
|
||||||
v-if="isShowConditionsModal"
|
v-if="isShowConditionsModal"
|
||||||
:to="modalContainerEl"
|
:to="modalContainerEl"
|
||||||
@@ -261,6 +273,7 @@ import FieldType from './field_type'
|
|||||||
import PaymentSettings from './payment_settings'
|
import PaymentSettings from './payment_settings'
|
||||||
import FieldSettings from './field_settings'
|
import FieldSettings from './field_settings'
|
||||||
import FormulaModal from './formula_modal'
|
import FormulaModal from './formula_modal'
|
||||||
|
import FontModal from './font_modal'
|
||||||
import ConditionsModal from './conditions_modal'
|
import ConditionsModal from './conditions_modal'
|
||||||
import DescriptionModal from './description_modal'
|
import DescriptionModal from './description_modal'
|
||||||
import { IconRouteAltLeft, IconMathFunction, IconNewSection, IconTrashX, IconSettings } from '@tabler/icons-vue'
|
import { IconRouteAltLeft, IconMathFunction, IconNewSection, IconTrashX, IconSettings } from '@tabler/icons-vue'
|
||||||
@@ -275,6 +288,7 @@ export default {
|
|||||||
PaymentSettings,
|
PaymentSettings,
|
||||||
IconNewSection,
|
IconNewSection,
|
||||||
FormulaModal,
|
FormulaModal,
|
||||||
|
FontModal,
|
||||||
DescriptionModal,
|
DescriptionModal,
|
||||||
ConditionsModal,
|
ConditionsModal,
|
||||||
IconRouteAltLeft,
|
IconRouteAltLeft,
|
||||||
@@ -305,6 +319,7 @@ export default {
|
|||||||
isNameFocus: false,
|
isNameFocus: false,
|
||||||
showPaymentModal: false,
|
showPaymentModal: false,
|
||||||
isShowFormulaModal: false,
|
isShowFormulaModal: false,
|
||||||
|
isShowFontModal: false,
|
||||||
isShowConditionsModal: false,
|
isShowConditionsModal: false,
|
||||||
isShowDescriptionModal: false,
|
isShowDescriptionModal: false,
|
||||||
renderDropdown: false
|
renderDropdown: false
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="['number', 'cells'].includes(field.type)"
|
v-if="['cells'].includes(field.type)"
|
||||||
class="py-1.5 px-1 relative"
|
class="py-1.5 px-1 relative"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
@@ -330,6 +330,19 @@
|
|||||||
v-if="field.type != 'stamp'"
|
v-if="field.type != 'stamp'"
|
||||||
class="pb-0.5 mt-0.5"
|
class="pb-0.5 mt-0.5"
|
||||||
>
|
>
|
||||||
|
<li v-if="['text', 'number', 'date'].includes(field.type)">
|
||||||
|
<label
|
||||||
|
class="label-text cursor-pointer text-center w-full flex items-center"
|
||||||
|
@click="$emit('click-font')"
|
||||||
|
>
|
||||||
|
<IconTypography
|
||||||
|
width="18"
|
||||||
|
/>
|
||||||
|
<span class="text-sm">
|
||||||
|
{{ t('font') }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="field.type != 'stamp'"
|
v-if="field.type != 'stamp'"
|
||||||
>
|
>
|
||||||
@@ -427,7 +440,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { IconRouteAltLeft, IconShape, IconX, IconMathFunction, IconNewSection, IconInfoCircle, IconCopy } from '@tabler/icons-vue'
|
import { IconRouteAltLeft, IconTypography, IconShape, IconX, IconMathFunction, IconNewSection, IconInfoCircle, IconCopy } from '@tabler/icons-vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FieldSettings',
|
name: 'FieldSettings',
|
||||||
@@ -438,6 +451,7 @@ export default {
|
|||||||
IconRouteAltLeft,
|
IconRouteAltLeft,
|
||||||
IconCopy,
|
IconCopy,
|
||||||
IconNewSection,
|
IconNewSection,
|
||||||
|
IconTypography,
|
||||||
IconX
|
IconX
|
||||||
},
|
},
|
||||||
inject: ['template', 'save', 't'],
|
inject: ['template', 'save', 't'],
|
||||||
@@ -472,7 +486,7 @@ export default {
|
|||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['set-draw', 'scroll-to', 'click-formula', 'click-description', 'click-condition', 'remove-area'],
|
emits: ['set-draw', 'scroll-to', 'click-formula', 'click-description', 'click-condition', 'click-font', 'remove-area'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@touchstart="renderDropdown = true"
|
@touchstart="renderDropdown = true"
|
||||||
>
|
>
|
||||||
<div class="flex space-x-2 items-end">
|
<div class="flex space-x-2 items-end">
|
||||||
<div class="group/contenteditable-container bg-base-100 rounded-md p-2 border border-base-300 w-full flex justify-between items-end">
|
<div class="group/contenteditable-container bg-base-100 rounded-md p-2 border border-base-300 w-full flex justify-between items-end roles-dropdown-label-mobile">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<span
|
<span
|
||||||
class="w-3 h-3 flex-shrink-0 rounded-full"
|
class="w-3 h-3 flex-shrink-0 rounded-full"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown dropdown-top dropdown-end">
|
<div class="dropdown dropdown-top dropdown-end roles-dropdown-mobile">
|
||||||
<label
|
<label
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="bg-base-100 cursor-pointer rounded-md p-2 border border-base-300 w-full flex justify-center"
|
class="bg-base-100 cursor-pointer rounded-md p-2 border border-base-300 w-full flex justify-center"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
class="dropdown"
|
class="dropdown field-types-dropdown"
|
||||||
@mouseenter="renderDropdown = true"
|
@mouseenter="renderDropdown = true"
|
||||||
@touchstart="renderDropdown = true"
|
@touchstart="renderDropdown = true"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div :class="withStickySubmitters ? 'sticky top-0 z-[1]' : ''">
|
<div :class="withStickySubmitters ? 'sticky top-0 z-[1]' : ''">
|
||||||
<FieldSubmitter
|
<FieldSubmitter
|
||||||
:model-value="selectedSubmitter.uuid"
|
:model-value="selectedSubmitter.uuid"
|
||||||
class="roles-dropdown w-full rounded-lg"
|
class="roles-dropdown w-full rounded-lg roles-dropdown"
|
||||||
:style="withStickySubmitters ? { backgroundColor } : {}"
|
:style="withStickySubmitters ? { backgroundColor } : {}"
|
||||||
:submitters="submitters"
|
:submitters="submitters"
|
||||||
:menu-style="{ overflow: 'auto', display: 'flex', flexDirection: 'row', maxHeight: 'calc(100vh - 120px)', backgroundColor: ['', null, 'transparent'].includes(backgroundColor) ? 'white' : backgroundColor }"
|
:menu-style="{ overflow: 'auto', display: 'flex', flexDirection: 'row', maxHeight: 'calc(100vh - 120px)', backgroundColor: ['', null, 'transparent'].includes(backgroundColor) ? 'white' : backgroundColor }"
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
<div
|
<div
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
class="default-field border border-base-300 rounded rounded-tr-none relative group mb-2"
|
class="border border-base-300 rounded rounded-tr-none relative group mb-2 default-field fields-list-item"
|
||||||
@dragstart="onDragstart({ type: 'text', ...field })"
|
@dragstart="onDragstart({ type: 'text', ...field })"
|
||||||
@dragend="$emit('drag-end')"
|
@dragend="$emit('drag-end')"
|
||||||
>
|
>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="editable && !onlyDefinedFields"
|
v-if="editable && !onlyDefinedFields"
|
||||||
class="grid grid-cols-3 gap-1 pb-2"
|
class="grid grid-cols-3 gap-1 pb-2 fields-grid"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(icon, type) in fieldIconsSorted"
|
v-for="(icon, type) in fieldIconsSorted"
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="(fieldTypes.length === 0 || fieldTypes.includes(type)) && (withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification')"
|
v-if="(fieldTypes.length === 0 || fieldTypes.includes(type)) && (withPhone || type != 'phone') && (withPayment || type != 'payment') && (withVerification || type != 'verification')"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
class="field-type-button group flex items-center justify-center border border-dashed w-full rounded relative"
|
class="field-type-button group flex items-center justify-center border border-dashed w-full rounded relative fields-grid-item"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
:class="drawFieldType === type ? 'border-base-content/40' : 'border-base-300 hover:border-base-content/20'"
|
:class="drawFieldType === type ? 'border-base-content/40' : 'border-base-300 hover:border-base-content/20'"
|
||||||
@dragstart="onDragstart({ type: type })"
|
@dragstart="onDragstart({ type: type })"
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
<a
|
<a
|
||||||
href="https://www.docuseal.com/pricing"
|
href="https://www.docuseal.com/pricing"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="opacity-50 flex items-center justify-center border border-dashed border-base-300 w-full rounded relative"
|
class="opacity-50 flex items-center justify-center border border-dashed border-base-300 w-full rounded relative fields-grid-item"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
>
|
>
|
||||||
<div class="w-0 absolute left-0">
|
<div class="w-0 absolute left-0">
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
<a
|
<a
|
||||||
href="https://www.docuseal.com/contact"
|
href="https://www.docuseal.com/contact"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="opacity-50 flex items-center justify-center border border-dashed border-base-300 w-full rounded relative"
|
class="opacity-50 flex items-center justify-center border border-dashed border-base-300 w-full rounded relative fields-grid-item"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
>
|
>
|
||||||
<div class="w-0 absolute left-0">
|
<div class="w-0 absolute left-0">
|
||||||
@@ -229,6 +229,11 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
withFieldsSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
template: {
|
template: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
@@ -297,7 +302,11 @@ export default {
|
|||||||
fieldNames: FieldType.computed.fieldNames,
|
fieldNames: FieldType.computed.fieldNames,
|
||||||
fieldIcons: FieldType.computed.fieldIcons,
|
fieldIcons: FieldType.computed.fieldIcons,
|
||||||
isShowFieldSearch () {
|
isShowFieldSearch () {
|
||||||
return this.submitterDefaultFields.length > 15
|
if (this.withFieldsSearch === false) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return this.submitterDefaultFields.length > 15
|
||||||
|
}
|
||||||
},
|
},
|
||||||
defaultFieldsIndex () {
|
defaultFieldsIndex () {
|
||||||
return this.defaultFields.reduce((acc, field) => {
|
return this.defaultFields.reduce((acc, field) => {
|
||||||
|
|||||||
@@ -0,0 +1,290 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="modal modal-open items-start !animate-none overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute top-0 bottom-0 right-0 left-0"
|
||||||
|
@click.prevent="$emit('close')"
|
||||||
|
/>
|
||||||
|
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||||
|
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||||
|
<span class="modal-title">
|
||||||
|
{{ t('font') }} - {{ field.name || buildDefaultName(field, template.fields) }}
|
||||||
|
</span>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="text-xl modal-close-button"
|
||||||
|
@click.prevent="$emit('close')"
|
||||||
|
>×</a>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center space-x-1.5">
|
||||||
|
<span>
|
||||||
|
<div class="dropdown modal-field-font-dropdown">
|
||||||
|
<label
|
||||||
|
tabindex="0"
|
||||||
|
class="base-input flex items-center justify-between"
|
||||||
|
style="height: 32px; padding-right: 0; width: 120px"
|
||||||
|
:class="fonts.find((f) => f.value === preferences.font)?.class"
|
||||||
|
>
|
||||||
|
<span style="margin-top: 1px">
|
||||||
|
{{ preferences.font || 'Default' }}
|
||||||
|
</span>
|
||||||
|
<IconChevronDown
|
||||||
|
class="ml-2 mr-2 mt-0.5"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
tabindex="0"
|
||||||
|
class="dropdown-content p-0 mt-1 block z-10 menu shadow bg-white border border-base-300 rounded-md w-52"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(font, index) in fonts"
|
||||||
|
:key="index"
|
||||||
|
:value="font.value"
|
||||||
|
:class="{ 'bg-base-300': preferences.font == font.value, [font.class]: true }"
|
||||||
|
class="hover:bg-base-300 px-2 py-1.5 cursor-pointer"
|
||||||
|
@click="[font.value ? preferences.font = font.value : delete preferences.font, closeDropdown()]"
|
||||||
|
>
|
||||||
|
{{ font.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class="relative">
|
||||||
|
<select
|
||||||
|
class="select input-bordered bg-white select-sm text-center pl-2"
|
||||||
|
style="font-size: 16px; width: 86px; text-align-last: center;"
|
||||||
|
@change="$event.target.value ? preferences.font_size = parseInt($event.target.value) : delete preferences.font_size"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
:selected="!preferences.font_size"
|
||||||
|
value=""
|
||||||
|
>
|
||||||
|
Auto
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="size in sizes"
|
||||||
|
:key="size"
|
||||||
|
:value="size"
|
||||||
|
:selected="size === preferences.font_size"
|
||||||
|
>
|
||||||
|
{{ size }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<span
|
||||||
|
class="border-l pl-1.5 absolute bg-white bottom-0 pointer-events-none text-sm h-5"
|
||||||
|
style="right: 13px; top: 7px"
|
||||||
|
>
|
||||||
|
pt
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<div
|
||||||
|
class="join"
|
||||||
|
style="height: 32px"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="(type, index) in types"
|
||||||
|
:key="index"
|
||||||
|
class="btn btn-sm join-item bg-white input-bordered hover:border-base-content/20 hover:bg-base-200/50 px-2"
|
||||||
|
:class="{ '!bg-base-300': preferences.font_type?.includes(type.value) }"
|
||||||
|
@click="setFontType(type.value)"
|
||||||
|
>
|
||||||
|
<component :is="type.icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<div
|
||||||
|
class="join"
|
||||||
|
style="height: 32px"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="(align, index) in aligns"
|
||||||
|
:key="index"
|
||||||
|
class="btn btn-sm join-item bg-white input-bordered hover:border-base-content/20 hover:bg-base-200/50 px-2"
|
||||||
|
:class="{ '!bg-base-300': preferences.align === align.value }"
|
||||||
|
@click="align.value && preferences.align != align.value ? preferences.align = align.value : delete preferences.align"
|
||||||
|
>
|
||||||
|
<component :is="align.icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<select
|
||||||
|
class="input input-bordered bg-white input-sm text-lg rounded-md"
|
||||||
|
style="-webkit-appearance: none; -moz-appearance: none; text-indent: 0px; text-overflow: ''; padding: 0px 6px; height: 32px"
|
||||||
|
@change="$event.target.value ? preferences.color = $event.target.value : delete preferences.color"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(color, index) in colors"
|
||||||
|
:key="index"
|
||||||
|
:value="color.value"
|
||||||
|
:selected="color.value == preferences.color"
|
||||||
|
>
|
||||||
|
{{ color.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div
|
||||||
|
class="flex items-center border border-base-content/20 rounded-xl bg-white px-4 h-16 modal-field-font-preview"
|
||||||
|
:style="{
|
||||||
|
color: preferences.color || 'black',
|
||||||
|
fontSize: (preferences.font_size || 12) + 'pt',
|
||||||
|
}"
|
||||||
|
:class="textClasses"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
contenteditable="true"
|
||||||
|
class="outline-none"
|
||||||
|
>
|
||||||
|
{{ field.default_value || field.name || buildDefaultName(field, template.fields) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<button
|
||||||
|
class="base-button w-full modal-save-button"
|
||||||
|
@click.prevent="saveAndClose"
|
||||||
|
>
|
||||||
|
{{ t('save') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { IconChevronDown, IconBold, IconItalic, IconAlignLeft, IconAlignRight, IconAlignCenter } from '@tabler/icons-vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FontModal',
|
||||||
|
components: {
|
||||||
|
IconChevronDown
|
||||||
|
},
|
||||||
|
inject: ['t', 'save', 'template'],
|
||||||
|
props: {
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
editable: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
buildDefaultName: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['close'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
preferences: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fonts () {
|
||||||
|
return [
|
||||||
|
{ value: null, label: 'Default' },
|
||||||
|
{ value: 'Times', label: 'Times', class: 'font-serif' },
|
||||||
|
{ value: 'Courier', label: 'Courier', class: 'font-mono' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
types () {
|
||||||
|
return [
|
||||||
|
{ icon: IconBold, value: 'bold' },
|
||||||
|
{ icon: IconItalic, value: 'italic' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
aligns () {
|
||||||
|
return [
|
||||||
|
{ icon: IconAlignLeft, value: 'left' },
|
||||||
|
{ icon: IconAlignCenter, value: 'center' },
|
||||||
|
{ icon: IconAlignRight, value: 'right' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sizes () {
|
||||||
|
return [...Array(23).keys()].map(i => i + 6)
|
||||||
|
},
|
||||||
|
colors () {
|
||||||
|
return [
|
||||||
|
{ label: '⬛', value: 'black' },
|
||||||
|
{ label: '🟦', value: 'blue' },
|
||||||
|
{ label: '🟥', value: 'red' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
textClasses () {
|
||||||
|
return {
|
||||||
|
'font-mono': this.preferences.font === 'Courier',
|
||||||
|
'font-serif': this.preferences.font === 'Times',
|
||||||
|
'justify-center': this.preferences.align === 'center',
|
||||||
|
'justify-start': this.preferences.align === 'left',
|
||||||
|
'justify-end': this.preferences.align === 'right',
|
||||||
|
'font-bold': ['bold_italic', 'bold'].includes(this.preferences.font_type),
|
||||||
|
italic: ['bold_italic', 'italic'].includes(this.preferences.font_type)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keys () {
|
||||||
|
return ['font_type', 'font_size', 'color', 'align', 'font']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.preferences = this.keys.reduce((acc, key) => {
|
||||||
|
acc[key] = this.field.preferences?.[key]
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeDropdown () {
|
||||||
|
this.$el.getRootNode().activeElement.blur()
|
||||||
|
},
|
||||||
|
setFontType (value) {
|
||||||
|
if (value === 'bold') {
|
||||||
|
if (this.preferences.font_type === 'bold') {
|
||||||
|
delete this.preferences.font_type
|
||||||
|
} else if (this.preferences.font_type === 'italic') {
|
||||||
|
this.preferences.font_type = 'bold_italic'
|
||||||
|
} else if (this.preferences.font_type === 'bold_italic') {
|
||||||
|
this.preferences.font_type = 'italic'
|
||||||
|
} else {
|
||||||
|
this.preferences.font_type = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'italic') {
|
||||||
|
if (this.preferences.font_type === 'italic') {
|
||||||
|
delete this.preferences.font_type
|
||||||
|
} else if (this.preferences.font_type === 'bold') {
|
||||||
|
this.preferences.font_type = 'bold_italic'
|
||||||
|
} else if (this.preferences.font_type === 'bold_italic') {
|
||||||
|
this.preferences.font_type = 'bold'
|
||||||
|
} else {
|
||||||
|
this.preferences.font_type = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveAndClose () {
|
||||||
|
this.field.preferences ||= {}
|
||||||
|
|
||||||
|
this.keys.forEach((key) => delete this.field.preferences[key])
|
||||||
|
|
||||||
|
Object.assign(this.field.preferences, this.preferences)
|
||||||
|
|
||||||
|
this.save()
|
||||||
|
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -8,12 +8,12 @@
|
|||||||
/>
|
/>
|
||||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||||
<span>
|
<span class="modal-title">
|
||||||
{{ t('formula') }} - {{ field.name || buildDefaultName(field, template.fields) }}
|
{{ t('formula') }} - {{ field.name || buildDefaultName(field, template.fields) }}
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="text-xl"
|
class="text-xl modal-close-button"
|
||||||
@click.prevent="$emit('close')"
|
@click.prevent="$emit('close')"
|
||||||
>×</a>
|
>×</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
class="base-button w-full"
|
class="base-button w-full modal-save-button"
|
||||||
@click.prevent="validateSaveAndClose"
|
@click.prevent="validateSaveAndClose"
|
||||||
>
|
>
|
||||||
{{ t('save') }}
|
{{ t('save') }}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const en = {
|
const en = {
|
||||||
|
font: 'Font',
|
||||||
party: 'Party',
|
party: 'Party',
|
||||||
method: 'Method',
|
method: 'Method',
|
||||||
reorder_fields: 'Reorder fields',
|
reorder_fields: 'Reorder fields',
|
||||||
@@ -161,6 +162,7 @@ const en = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const es = {
|
const es = {
|
||||||
|
fuente: 'Fuente',
|
||||||
party: 'Parte',
|
party: 'Parte',
|
||||||
method: 'Método',
|
method: 'Método',
|
||||||
reorder_fields: 'Reordenar campos',
|
reorder_fields: 'Reordenar campos',
|
||||||
@@ -323,6 +325,7 @@ const es = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const it = {
|
const it = {
|
||||||
|
font: 'Carattere',
|
||||||
party: 'Parte',
|
party: 'Parte',
|
||||||
method: 'Metodo',
|
method: 'Metodo',
|
||||||
reorder_fields: 'Riordina i campi',
|
reorder_fields: 'Riordina i campi',
|
||||||
@@ -485,6 +488,7 @@ const it = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pt = {
|
const pt = {
|
||||||
|
fonte: 'Fonte',
|
||||||
party: 'Parte',
|
party: 'Parte',
|
||||||
method: 'Método',
|
method: 'Método',
|
||||||
reorder_fields: 'Reorganizar campos',
|
reorder_fields: 'Reorganizar campos',
|
||||||
@@ -647,6 +651,7 @@ const pt = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fr = {
|
const fr = {
|
||||||
|
font: 'Police',
|
||||||
party: 'Partie',
|
party: 'Partie',
|
||||||
method: 'Méthode',
|
method: 'Méthode',
|
||||||
reorder_fields: 'Réorganiser les champs',
|
reorder_fields: 'Réorganiser les champs',
|
||||||
@@ -809,6 +814,7 @@ const fr = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const de = {
|
const de = {
|
||||||
|
font: 'Schriftart',
|
||||||
party: 'Partei',
|
party: 'Partei',
|
||||||
method: 'Verfahren',
|
method: 'Verfahren',
|
||||||
reorder_fields: 'Felder neu anordnen',
|
reorder_fields: 'Felder neu anordnen',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="absolute text-center w-full bottom-0 pr-3 mb-4">
|
<div class="absolute text-center w-full bottom-0 pr-3 mb-4">
|
||||||
<span class="w-full bg-base-200 px-4 py-2 rounded-md inline-flex space-x-2 mx-auto items-center justify-between mb-2 z-20">
|
<span class="w-full bg-base-200 px-4 py-2 rounded-md inline-flex space-x-2 mx-auto items-center justify-between mb-2 z-20 draw-field-container-mobile">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<component
|
<component
|
||||||
:is="fieldIcons[drawField.type]"
|
:is="fieldIcons[drawField.type]"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<span
|
<span class="dropdown dropdown-top dropdown-end absolute bottom-4 right-4 z-10 fields-dropdown-mobile">
|
||||||
class="dropdown dropdown-top dropdown-end absolute bottom-4 right-4 z-10"
|
|
||||||
>
|
|
||||||
<label
|
<label
|
||||||
class="btn btn-neutral text-white btn-circle btn-lg group"
|
class="btn btn-neutral text-white btn-circle btn-lg group"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
class="dropdown dropdown-end"
|
class="dropdown dropdown-end field-settings-dropdown"
|
||||||
:class="{ 'dropdown-open': ((!field.preferences?.price && !field.preferences?.formula) || !isConnected) && !isLoading }"
|
:class="{ 'dropdown-open': ((!field.preferences?.price && !field.preferences?.formula) || !isConnected) && !isLoading }"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0 document-control-button"
|
||||||
@click.stop="isShowConditionsModal = true"
|
@click.stop="isShowConditionsModal = true"
|
||||||
>
|
>
|
||||||
<IconRouteAltLeft
|
<IconRouteAltLeft
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
||||||
style="width: 24px; height: 24px"
|
style="width: 24px; height: 24px"
|
||||||
@click.stop="$emit('remove', item)"
|
@click.stop="$emit('remove', item)"
|
||||||
>
|
>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
class="tooltip tooltip-left before:text-xs"
|
class="tooltip tooltip-left before:text-xs"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0 document-control-button"
|
||||||
@click.stop="$emit('reorder', item)"
|
@click.stop="$emit('reorder', item)"
|
||||||
>
|
>
|
||||||
<IconSortDescending2
|
<IconSortDescending2
|
||||||
@@ -75,14 +75,14 @@
|
|||||||
</span>
|
</span>
|
||||||
<template v-if="withArrows">
|
<template v-if="withArrows">
|
||||||
<button
|
<button
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
||||||
style="width: 24px; height: 24px"
|
style="width: 24px; height: 24px"
|
||||||
@click.stop="$emit('up', item)"
|
@click.stop="$emit('up', item)"
|
||||||
>
|
>
|
||||||
↑
|
↑
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
|
class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
||||||
style="width: 24px; height: 24px"
|
style="width: 24px; height: 24px"
|
||||||
@click.stop="$emit('down', item)"
|
@click.stop="$emit('down', item)"
|
||||||
>
|
>
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex pb-2 pt-1.5">
|
<div class="flex pb-2 pt-1.5 document-preview-name">
|
||||||
<Contenteditable
|
<Contenteditable
|
||||||
:model-value="item.name"
|
:model-value="item.name"
|
||||||
:icon-width="16"
|
:icon-width="16"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-neutral btn-xs text-white transition-none"
|
class="btn btn-neutral btn-xs text-white transition-none replace-document-button"
|
||||||
:class="{ 'opacity-100': isLoading || isProcessing }"
|
:class="{ 'opacity-100': isLoading || isProcessing }"
|
||||||
>
|
>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<label
|
<label
|
||||||
id="add_document_button"
|
id="add_document_button"
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-outline w-full"
|
class="btn btn-outline w-full add-document-button"
|
||||||
:class="{ 'btn-disabled': isLoading || isProcessing }"
|
:class="{ 'btn-disabled': isLoading || isProcessing }"
|
||||||
>
|
>
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ProcessSubmissionExpiredJob
|
||||||
|
include Sidekiq::Job
|
||||||
|
|
||||||
|
def perform(params = {})
|
||||||
|
submission = Submission.find(params['submission_id'])
|
||||||
|
|
||||||
|
return if submission.archived_at?
|
||||||
|
return if submission.template.archived_at?
|
||||||
|
return if submission.submitters.where.not(declined_at: nil).exists?
|
||||||
|
return unless submission.submitters.exists?(completed_at: nil)
|
||||||
|
|
||||||
|
WebhookUrls.for_account_id(submission.account_id, %w[submission.expired]).each do |webhook|
|
||||||
|
SendSubmissionExpiredWebhookRequestJob.perform_async('submission_id' => submission.id,
|
||||||
|
'webhook_url_id' => webhook.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,7 +5,7 @@ class SendFormCompletedWebhookRequestJob
|
|||||||
|
|
||||||
sidekiq_options queue: :webhooks
|
sidekiq_options queue: :webhooks
|
||||||
|
|
||||||
MAX_ATTEMPTS = 20
|
MAX_ATTEMPTS = 12
|
||||||
|
|
||||||
def perform(params = {})
|
def perform(params = {})
|
||||||
submitter = Submitter.find(params['submitter_id'])
|
submitter = Submitter.find(params['submitter_id'])
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SendSubmissionExpiredWebhookRequestJob
|
||||||
|
include Sidekiq::Job
|
||||||
|
|
||||||
|
sidekiq_options queue: :webhooks
|
||||||
|
|
||||||
|
MAX_ATTEMPTS = 10
|
||||||
|
|
||||||
|
def perform(params = {})
|
||||||
|
submission = Submission.find(params['submission_id'])
|
||||||
|
webhook_url = WebhookUrl.find(params['webhook_url_id'])
|
||||||
|
|
||||||
|
attempt = params['attempt'].to_i
|
||||||
|
|
||||||
|
return if webhook_url.url.blank? || webhook_url.events.exclude?('submission.expired')
|
||||||
|
|
||||||
|
resp = SendWebhookRequest.call(webhook_url, event_type: 'submission.expired',
|
||||||
|
data: Submissions::SerializeForApi.call(submission))
|
||||||
|
|
||||||
|
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
|
||||||
|
(!Docuseal.multitenant? || submission.account.account_configs.exists?(key: :plan))
|
||||||
|
SendSubmissionExpiredWebhookRequestJob.perform_in((2**attempt).minutes, {
|
||||||
|
'submission_id' => submission.id,
|
||||||
|
'webhook_url_id' => webhook_url.id,
|
||||||
|
'attempt' => attempt + 1,
|
||||||
|
'last_status' => resp&.status.to_i
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,6 +8,12 @@ class SendSubmitterInvitationEmailJob
|
|||||||
|
|
||||||
return if submitter.submission.source == 'invite' && !Accounts.can_send_emails?(submitter.account, on_events: true)
|
return if submitter.submission.source == 'invite' && !Accounts.can_send_emails?(submitter.account, on_events: true)
|
||||||
|
|
||||||
|
unless Accounts.can_send_invitation_emails?(submitter.account)
|
||||||
|
Rollbar.warning("Skip email: #{submitter.account.id}") if defined?(Rollbar)
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
mail = SubmitterMailer.invitation_email(submitter)
|
mail = SubmitterMailer.invitation_email(submitter)
|
||||||
|
|
||||||
Submitters::ValidateSending.call(submitter, mail)
|
Submitters::ValidateSending.call(submitter, mail)
|
||||||
@@ -17,6 +23,6 @@ class SendSubmitterInvitationEmailJob
|
|||||||
SubmissionEvent.create!(submitter:, event_type: 'send_email')
|
SubmissionEvent.create!(submitter:, event_type: 'send_email')
|
||||||
|
|
||||||
submitter.sent_at ||= Time.current
|
submitter.sent_at ||= Time.current
|
||||||
submitter.save
|
submitter.save!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -14,8 +14,20 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
@email_message = submitter.account.email_messages.find_by(uuid: submitter.preferences['email_message_uuid'])
|
@email_message = submitter.account.email_messages.find_by(uuid: submitter.preferences['email_message_uuid'])
|
||||||
end
|
end
|
||||||
|
|
||||||
@body = @email_message&.body.presence || @submitter.template.preferences['request_email_body'].presence
|
template_submitters_index =
|
||||||
@subject = @email_message&.subject.presence || @submitter.template.preferences['request_email_subject'].presence
|
if @email_message.blank?
|
||||||
|
build_submitter_preferences_index(@submitter)
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
@body = @email_message&.body.presence ||
|
||||||
|
template_submitters_index.dig(@submitter.uuid, 'request_email_body').presence ||
|
||||||
|
@submitter.template.preferences['request_email_body'].presence
|
||||||
|
|
||||||
|
@subject = @email_message&.subject.presence ||
|
||||||
|
template_submitters_index.dig(@submitter.uuid, 'request_email_subject').presence ||
|
||||||
|
@submitter.template.preferences['request_email_subject'].presence
|
||||||
|
|
||||||
@email_config = AccountConfigs.find_for_account(@current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY)
|
@email_config = AccountConfigs.find_for_account(@current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY)
|
||||||
|
|
||||||
@@ -24,14 +36,7 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
reply_to = build_submitter_reply_to(@submitter)
|
reply_to = build_submitter_reply_to(@submitter)
|
||||||
|
|
||||||
I18n.with_locale(@current_account.locale) do
|
I18n.with_locale(@current_account.locale) do
|
||||||
subject =
|
subject = build_invite_subject(@subject, @email_config, submitter)
|
||||||
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(
|
mail(
|
||||||
to: @submitter.friendly_name,
|
to: @submitter.friendly_name,
|
||||||
@@ -196,6 +201,20 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name
|
user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_invite_subject(subject, email_config, submitter)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_submitter_preferences_index(submitter)
|
||||||
|
submitter.template.preferences['submitters'].to_a.index_by { |e| e['uuid'] }
|
||||||
|
end
|
||||||
|
|
||||||
def add_attachments_with_size_limit(submitter, storage_attachments, current_size, filename_format = nil)
|
def add_attachments_with_size_limit(submitter, storage_attachments, current_size, filename_format = nil)
|
||||||
total_size = current_size
|
total_size = current_size
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class Submitter < ApplicationRecord
|
|||||||
has_many_attached :attachments
|
has_many_attached :attachments
|
||||||
has_many_attached :preview_documents
|
has_many_attached :preview_documents
|
||||||
has_many :template_accesses, through: :template
|
has_many :template_accesses, through: :template
|
||||||
|
has_many :email_events, as: :emailable, dependent: (Docuseal.multitenant? ? nil : :destroy)
|
||||||
|
|
||||||
has_many :document_generation_events, dependent: :destroy
|
has_many :document_generation_events, dependent: :destroy
|
||||||
has_many :submission_events, dependent: :destroy
|
has_many :submission_events, dependent: :destroy
|
||||||
@@ -63,6 +64,8 @@ class Submitter < ApplicationRecord
|
|||||||
|
|
||||||
scope :completed, -> { where.not(completed_at: nil) }
|
scope :completed, -> { where.not(completed_at: nil) }
|
||||||
|
|
||||||
|
after_destroy :anonymize_email_events, if: -> { Docuseal.multitenant? }
|
||||||
|
|
||||||
def status
|
def status
|
||||||
if declined_at?
|
if declined_at?
|
||||||
'declined'
|
'declined'
|
||||||
@@ -108,4 +111,12 @@ class Submitter < ApplicationRecord
|
|||||||
fields.any? { |f| f['submitter_uuid'] == uuid && signature_field_types.include?(f['type']) }
|
fields.any? { |f| f['submitter_uuid'] == uuid && signature_field_types.include?(f['type']) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def anonymize_email_events
|
||||||
|
email_events.each do |event|
|
||||||
|
event.update!(email: Digest::MD5.base64digest(event.email))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -28,10 +28,12 @@ class WebhookUrl < ApplicationRecord
|
|||||||
form.started
|
form.started
|
||||||
form.completed
|
form.completed
|
||||||
form.declined
|
form.declined
|
||||||
|
submission.created
|
||||||
|
submission.completed
|
||||||
|
submission.expired
|
||||||
|
submission.archived
|
||||||
template.created
|
template.created
|
||||||
template.updated
|
template.updated
|
||||||
submission.created
|
|
||||||
submission.archived
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<span>
|
<span>
|
||||||
<%= t('add_signature_id_to_the_documents') %>
|
<%= t('add_signature_id_to_the_documents') %>
|
||||||
</span>
|
</span>
|
||||||
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
|
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()', disabled: can?(:manage, :cfr) %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<span>
|
<span>
|
||||||
<%= t('require_signing_reason') %>
|
<%= t('require_signing_reason') %>
|
||||||
</span>
|
</span>
|
||||||
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
|
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()', disabled: can?(:manage, :cfr) %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
|
||||||
|
<path d="M7 16h-4l3.47 -4.66a2 2 0 1 0 -3.47 -1.54"></path>
|
||||||
|
<path d="M10 16v-8h4"></path> <path d="M10 12l3 0"></path>
|
||||||
|
<path d="M17 16v-6a2 2 0 0 1 4 0v6"></path>
|
||||||
|
<path d="M17 13l4 0"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 426 B |
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 397 B |
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
|
||||||
|
<path d="M8 13v-8.5a1.5 1.5 0 0 1 3 0v7.5"></path>
|
||||||
|
<path d="M11 11.5v-2a1.5 1.5 0 0 1 3 0v2.5"></path>
|
||||||
|
<path d="M14 10.5a1.5 1.5 0 0 1 3 0v1.5"></path>
|
||||||
|
<path d="M17 11.5a1.5 1.5 0 0 1 3 0v4.5a6 6 0 0 1 -6 6h-2h.208a6 6 0 0 1 -5.012 -2.7l-.196 -.3c-.312 -.479 -1.407 -2.388 -3.286 -5.728a1.5 1.5 0 0 1 .536 -2.022a1.867 1.867 0 0 1 2.28 .28l1.47 1.47"></path>
|
||||||
|
<path d="M5 3l-1 -1"></path>
|
||||||
|
<path d="M4 7h-1"></path>
|
||||||
|
<path d="M14 3l1 -1"></path>
|
||||||
|
<path d="M15 6h1"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 711 B |
@@ -0,0 +1,11 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
|
||||||
|
<path d="M4 12h.01"></path>
|
||||||
|
<path d="M4 6h.01"></path>
|
||||||
|
<path d="M4 18h.01"></path>
|
||||||
|
<path d="M8 18h2"></path>
|
||||||
|
<path d="M8 12h2"></path>
|
||||||
|
<path d="M8 6h2"></path>
|
||||||
|
<path d="M14 6h6"></path>
|
||||||
|
<path d="M14 12h6"></path>
|
||||||
|
<path d="M14 18h6"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 484 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
|
||||||
|
<path d="M3 9l9 6l9 -6l-9 -6l-9 6"></path>
|
||||||
|
<path d="M21 9v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10"></path>
|
||||||
|
<path d="M3 19l6 -6"></path>
|
||||||
|
<path d="M15 13l6 6"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 401 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
|
||||||
|
<path d="M5 4h4l2 5l-2.5 1.5a11 11 0 0 0 5 5l1.5 -2.5l5 2v4a2 2 0 0 1 -2 2a16 16 0 0 1 -15 -15a2 2 0 0 1 2 -2"></path>
|
||||||
|
<path d="M15 6l2 2l4 -4"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 382 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="2">
|
||||||
|
<path d="M7 4v16l13 -8z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 261 B |
@@ -2,6 +2,9 @@
|
|||||||
<html data-theme="docuseal" lang="<%= I18n.locale %>">
|
<html data-theme="docuseal" lang="<%= I18n.locale %>">
|
||||||
<head>
|
<head>
|
||||||
<%= render 'layouts/head_tags' %>
|
<%= render 'layouts/head_tags' %>
|
||||||
|
<% if Docuseal.enable_pwa? %>
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<% end %>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<%= csp_meta_tag %>
|
<%= csp_meta_tag %>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "<%= Docuseal.product_name %>",
|
||||||
|
"short_name": "<%= Docuseal.product_name %>",
|
||||||
|
"id": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/logo.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/apple-touch-icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"orientation": "natural",
|
||||||
|
"description": "<%= Docuseal.product_name %> is an open source platform that provides secure and efficient digital document signing and processing.",
|
||||||
|
"categories": ["productivity", "utilities"],
|
||||||
|
"theme_color": "#FAF7F4",
|
||||||
|
"background_color": "#FAF7F4"
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="space-y-6 mx-auto">
|
<div class="space-y-6 mx-auto">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<%= render 'start_form/docuseal_logo' %>
|
<%= render 'start_form/banner' %>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center text-4xl font-bold">
|
<div class="text-center text-4xl font-bold">
|
||||||
<%= t('email_has_been_sent') %>
|
<%= t('email_has_been_sent') %>
|
||||||
|
|||||||
@@ -2,5 +2,9 @@
|
|||||||
---
|
---
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
<% if @current_account&.testing? %>
|
||||||
|
<%= t('sent_using_product_name_in_testing_mode_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
||||||
|
<% else %>
|
||||||
|
<%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %>
|
||||||
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
@@ -30,7 +30,9 @@
|
|||||||
<div dir="auto" class="form-control !mt-0">
|
<div dir="auto" class="form-control !mt-0">
|
||||||
<%= f.label :email, t('email'), class: 'label' %>
|
<%= f.label :email, t('email'), class: 'label' %>
|
||||||
<%= f.email_field :email, value: current_user&.email || params[:email] || @submitter.email, required: true, class: 'base-input', placeholder: t('provide_your_email_to_start') %>
|
<%= f.email_field :email, value: current_user&.email || params[:email] || @submitter.email, required: true, class: 'base-input', placeholder: t('provide_your_email_to_start') %>
|
||||||
<%= @error_message %>
|
<% if @error_message %>
|
||||||
|
<span class="label-text-alt text-red-400 mt-1"><%= @error_message %></span>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<toggle-submit dir="auto" class="form-control">
|
<toggle-submit dir="auto" class="form-control">
|
||||||
<%= f.button button_title(title: t('start'), disabled_with: t('starting')), class: 'base-button' %>
|
<%= f.button button_title(title: t('start'), disabled_with: t('starting')), class: 'base-button' %>
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<% event_colors = %w[bg-red-200 bg-sky-200 bg-emerald-200 bg-yellow-200 bg-purple-200 bg-pink-200 bg-cyan-200 bg-orange-200 bg-lime-200 bg-indigo-200] %>
|
||||||
|
<% submitters_uuids = (@submission.template_submitters || @submission.template.submitters).pluck('uuid') %>
|
||||||
|
<%= render 'shared/turbo_modal_large', title: t('event_log') do %>
|
||||||
|
<div class="pl-8 pr-4 py-4">
|
||||||
|
<ol class="relative border-s border-base-300 space-y-6">
|
||||||
|
<li class="ml-7">
|
||||||
|
<span class="absolute flex items-center justify-center w-7 h-7 rounded-full -start-3.5 ring-8 ring-base-100 text-base-content bg-gray-200">
|
||||||
|
<%= svg_icon('file_text', class: 'w-4 h-4') %>
|
||||||
|
</span>
|
||||||
|
<p class="text-sm leading-none text-base-content/60 pt-1.5">
|
||||||
|
<%= l(@submission.created_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %>
|
||||||
|
</p>
|
||||||
|
<p class="text-base-content/80 mt-1">
|
||||||
|
<% if @submission.source == 'invite' %>
|
||||||
|
<%= t('submission_created_by_email_html', email: @submission.created_by_user.email) %>
|
||||||
|
<% elsif @submission.created_by_user %>
|
||||||
|
<%= t('submission_created_by_email_via_source_html', email: @submission.created_by_user.email, source: t("submission_sources.#{@submission.source}")) %>
|
||||||
|
<% else %>
|
||||||
|
<%= t('submission_created_via_source_html', source: t("submission_sources.#{@submission.source}")) %>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<% @submission.submission_events.order(:event_timestamp).each do |event| %>
|
||||||
|
<% submitter = @submission.submitters.find { |e| e.id == event.submitter_id } %>
|
||||||
|
<% bg_class = event_colors[submitters_uuids.index(submitter.uuid) % event_colors.length] %>
|
||||||
|
<% submitter_name = event.event_type.include?('sms') || event.event_type.include?('phone') ? (event.data['phone'] || submitter.phone) : (submitter.name || submitter.email || submitter.phone) %>
|
||||||
|
<li class="ml-7">
|
||||||
|
<span class="absolute flex items-center justify-center w-7 h-7 rounded-full -start-3.5 ring-8 ring-base-100 text-base-content <%= bg_class %>">
|
||||||
|
<%= svg_icon(SubmissionEventsController::SUBMISSION_EVENT_ICONS.fetch(event.event_type, 'circle_dot'), class: 'w-4 h-4') %>
|
||||||
|
</span>
|
||||||
|
<p class="text-sm leading-none text-base-content/60 pt-1.5">
|
||||||
|
<%= l(event.event_timestamp.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %>
|
||||||
|
</p>
|
||||||
|
<p class="text-base-content/80 mt-1">
|
||||||
|
<% if event.event_type == 'complete_verification' %>
|
||||||
|
<%= t('submission_event_names.complete_verification_by_html', provider: event.data['method'], submitter_name:) %>
|
||||||
|
<% elsif event.event_type == 'invite_party' && (invited_submitter = @submission.submitters.find { |e| e.uuid == event.data['uuid'] }) && (name = @submission.template_submitters.find { |e| e['uuid'] == event.data['uuid'] }&.dig('name')) %>
|
||||||
|
<%= t('submission_event_names.invite_party_by_html', invited_submitter_name: [invited_submitter.name || invited_submitter.email || invited_submitter.phone, name].join(' '), submitter_name:) %>
|
||||||
|
<% elsif event.event_type.include?('send_') %>
|
||||||
|
<%= t("submission_event_names.#{event.event_type}_to_html", submitter_name:) %>
|
||||||
|
<% else %>
|
||||||
|
<%= t("submission_event_names.#{event.event_type}_by_html", submitter_name:) %>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<% submitter_preferences_index = template.preferences['submitters'].to_a.index_by { |e| e['uuid'] } %>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<% can_send_emails = Accounts.can_send_emails?(current_account) %>
|
<% can_send_emails = Accounts.can_send_emails?(current_account) %>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
@@ -36,10 +37,18 @@
|
|||||||
<% config = AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY) %>
|
<% config = AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY) %>
|
||||||
<div id="message_field" class="card card-compact bg-base-300/40 hidden">
|
<div id="message_field" class="card card-compact bg-base-300/40 hidden">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-control space-y-2">
|
<%= tag.input id: toggle_uuid = SecureRandom.uuid, value: '1', name: 'request_email_per_submitter', class: 'peer', type: 'checkbox', hidden: true, checked: local_assigns[:message_per_submitter] != false && template.preferences['submitters'].to_a.size > 1 %>
|
||||||
|
<div class="peer-checked:hidden form-control space-y-2">
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= f.label :subject, t('subject'), class: 'label' %>
|
<div class="flex justify-between">
|
||||||
<%= 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' %>
|
<%= f.label :subject, t('subject'), class: 'label' %>
|
||||||
|
<% if template.submitters.size > 1 && template.submitters.size < 5 && local_assigns[:message_per_submitter] != false %>
|
||||||
|
<label for="<%= toggle_uuid %>" class="label underline">
|
||||||
|
<%= t('edit_per_party') %>
|
||||||
|
</label>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<%= f.text_field :subject, value: local_assigns[:submitter_email_message]&.subject.presence || submitter_preferences_index.dig(local_assigns[:submitter]&.uuid, 'request_email_subject').presence || template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -49,7 +58,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<autoresize-textarea>
|
<autoresize-textarea>
|
||||||
<%= 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' %>
|
<%= f.text_area :body, value: local_assigns[:submitter_email_message]&.body.presence || submitter_preferences_index.dig(local_assigns[:submitter]&.uuid, 'request_email_body').presence || template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
|
||||||
</autoresize-textarea>
|
</autoresize-textarea>
|
||||||
<% unless local_assigns.fetch(:disable_save_as_default_template_option, false) %>
|
<% unless local_assigns.fetch(:disable_save_as_default_template_option, false) %>
|
||||||
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
|
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
|
||||||
@@ -60,5 +69,44 @@
|
|||||||
</div>
|
</div>
|
||||||
<%= render 'submissions/message_fields' %>
|
<%= render 'submissions/message_fields' %>
|
||||||
</div>
|
</div>
|
||||||
|
<% if template.submitters.size > 1 && template.submitters.size < 5 && local_assigns[:message_per_submitter] != false %>
|
||||||
|
<div class="hidden peer-checked:block form-control space-y-2">
|
||||||
|
<% uuid = SecureRandom.uuid %>
|
||||||
|
<% options = template.submitters.map { |e| [e['name'], "request_email_#{uuid}_#{e['uuid']}"] } %>
|
||||||
|
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="flex relative px-1">
|
||||||
|
<ul class="tabs w-full flex flex-nowrap">
|
||||||
|
<% options.each_with_index do |(label, val), index| %>
|
||||||
|
<div class="w-full">
|
||||||
|
<%= f.radio_button :selected, val, checked: index.zero?, id: "#{val}_radio", data: { action: 'click:toggle-visible#trigger' }, class: 'hidden peer' %>
|
||||||
|
<%= f.label :selected, label, value: val, for: "#{val}_radio", class: 'tab w-full tab-lifted peer-checked:tab-active !bg-transparent' %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</toggle-visible>
|
||||||
|
<% template.submitters.each_with_index do |submitter, index| %>
|
||||||
|
<%= fields_for :submitter_preferences, nil, index: submitter['uuid'] do |ff| %>
|
||||||
|
<div id="request_email_<%= uuid %>_<%= submitter['uuid'] %>" class="<%= 'hidden' if index != 0 %>">
|
||||||
|
<div class="form-control">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<%= ff.label :subject, t('subject'), class: 'label' %>
|
||||||
|
</div>
|
||||||
|
<%= ff.text_field :subject, value: local_assigns[:submitter_email_message]&.subject.presence || submitter_preferences_index.dig(submitter['uuid'], 'request_email_subject').presence || template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<%= ff.label :message, t('body'), class: 'label' %>
|
||||||
|
<span class="tooltip tooltip-right" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY].call['body'].scan(/{.*?}/).join(', ') %>">
|
||||||
|
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<autoresize-textarea>
|
||||||
|
<%= ff.text_area :body, value: local_assigns[:submitter_email_message]&.body.presence || submitter_preferences_index.dig(submitter['uuid'], 'request_email_body').presence || template.preferences['request_email_body'].presence || config.value['body'], required: true, class: 'base-textarea w-full', rows: 10, dir: 'auto' %>
|
||||||
|
</autoresize-textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
<% align = field.dig('preferences', 'align') %>
|
<% align = field.dig('preferences', 'align') %>
|
||||||
<% color = field.dig('preferences', 'color') %>
|
<% color = field.dig('preferences', 'color') %>
|
||||||
<field-value dir="auto" class="flex absolute text-[1.6vw] lg:text-base <%= 'font-serif' if field.dig('preferences', 'font') == 'Times' %> <%= align == 'right' ? 'justify-end' : (align == 'center' ? 'justify-center' : '') %>" style="<%= "color: #{color}; " if color.present? %>width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%; <%= "font-size: clamp(4pt, 1.6vw, #{field['preferences']['font_size'].to_i * 1.23}pt); line-height: `clamp(6pt, 2.0vw, #{(field['preferences']['font_size'].to_i * 1.23) + 3}pt)`" if field.dig('preferences', 'font_size') %>">
|
<% font = field.dig('preferences', 'font') %>
|
||||||
|
<% font_type = field.dig('preferences', 'font_type') %>
|
||||||
|
<field-value dir="auto" class="flex absolute text-[1.6vw] lg:text-base <%= 'font-mono' if font == 'Courier' %> <%= 'font-serif' if font == 'Times' %> <%= 'font-bold' if font_type == 'bold' || font_type == 'bold_italic' %> <%= 'italic' if font_type == 'italic' || font_type == 'bold_italic' %> <%= align == 'right' ? 'justify-end' : (align == 'center' ? 'justify-center' : '') %>" style="<%= "color: #{color}; " if color.present? %>width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%; <%= "font-size: clamp(4pt, 1.6vw, #{field['preferences']['font_size'].to_i * 1.23}pt); line-height: `clamp(6pt, 2.0vw, #{(field['preferences']['font_size'].to_i * 1.23) + 3}pt)`" if field.dig('preferences', 'font_size') %>">
|
||||||
<% if field['type'] == 'signature' %>
|
<% if field['type'] == 'signature' %>
|
||||||
<div class="flex flex-col justify-between h-full overflow-hidden">
|
<div class="flex flex-col justify-between h-full overflow-hidden">
|
||||||
<div class="flex-grow flex overflow-hidden" style="min-height: 50%">
|
<div class="flex-grow flex overflow-hidden" style="min-height: 50%">
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
<%= svg_icon('external_link', class: 'w-6 h-6') %>
|
<%= svg_icon('external_link', class: 'w-6 h-6') %>
|
||||||
<span class="hidden md:inline"><%= t('audit_log') %></span>
|
<span class="hidden md:inline"><%= t('audit_log') %></span>
|
||||||
</a>
|
</a>
|
||||||
|
<% elsif signed_in? %>
|
||||||
|
<%= link_to submission_events_path(@submission), class: 'white-button', data: { turbo_frame: :modal } do %>
|
||||||
|
<%= svg_icon('logs', class: 'w-6 h-6') %>
|
||||||
|
<span class="hidden md:block"><%= t('event_log') %></span>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if last_submitter %>
|
<% if last_submitter %>
|
||||||
<% if is_all_completed || !is_combined_enabled %>
|
<% if is_all_completed || !is_combined_enabled %>
|
||||||
@@ -100,7 +105,18 @@
|
|||||||
<% value = values[field['uuid']] %>
|
<% value = values[field['uuid']] %>
|
||||||
<% value ||= field['default_value'] if field['type'] == 'heading' %>
|
<% value ||= field['default_value'] if field['type'] == 'heading' %>
|
||||||
<% next if value.blank? %>
|
<% next if value.blank? %>
|
||||||
<%= render 'submissions/value', area:, field:, attachments_index:, value:, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
<% if (mask = field.dig('preferences', 'mask').presence) && signed_in? && can?(:read, @submission) %>
|
||||||
|
<span class="group">
|
||||||
|
<span class="hidden group-hover:inline">
|
||||||
|
<%= render 'submissions/value', area:, field:, attachments_index:, value:, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
||||||
|
</span>
|
||||||
|
<span class="group-hover:hidden">
|
||||||
|
<%= render 'submissions/value', area:, field:, attachments_index:, value: Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', '), locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<% else %>
|
||||||
|
<%= render 'submissions/value', area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -227,12 +243,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<% elsif field['type'] == 'checkbox' %>
|
<% elsif field['type'] == 'checkbox' %>
|
||||||
<%= svg_icon('check', class: 'w-6 h-6') %>
|
<%= svg_icon('check', class: 'w-6 h-6') %>
|
||||||
<% elsif field['type'] == 'number' %>
|
|
||||||
<%= NumberUtils.format_number(value, field.dig('preferences', 'format')) %>
|
|
||||||
<% elsif field['type'] == 'date' %>
|
|
||||||
<%= TimeUtils.format_date_string(value, field.dig('preferences', 'format'), @submission.account.locale) %>
|
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= Array.wrap(value).join(', ') %>
|
<% if field['type'] == 'number' %>
|
||||||
|
<% value = NumberUtils.format_number(value, field.dig('preferences', 'format')) %>
|
||||||
|
<% elsif field['type'] == 'date' %>
|
||||||
|
<% value = TimeUtils.format_date_string(value, field.dig('preferences', 'format'), @submission.account.locale) %>
|
||||||
|
<% end %>
|
||||||
|
<% if (mask = field.dig('preferences', 'mask').presence) %>
|
||||||
|
<% if signed_in? && can?(:read, @submission) %>
|
||||||
|
<div class="[&:not(:hover)]:after:content-[attr(data-value)] block whitespace-pre-wrap group" data-value="<%= Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') %>"><span class="hidden group-hover:block"><%= Array.wrap(value).join(', ') %></span></div>
|
||||||
|
<% else %>
|
||||||
|
<div class="whitespace-pre-wrap"><%= Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') %></div>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<div class="whitespace-pre-wrap"><%= Array.wrap(value).join(', ') %></div>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<% query_params = params.permit(:q).merge(filter_params) %>
|
<% query_params = params.permit(:q).merge(filter_params) %>
|
||||||
<% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit' }[params[:status]] %>
|
<% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit', 'sent' => 'send', 'opened' => 'mail_opened' }[params[:status]] %>
|
||||||
<div class="flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700">
|
<div class="flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700">
|
||||||
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||||
<%= svg_icon(icon, class: 'w-5 h-5 shrink-0') %>
|
<%= svg_icon(icon, class: 'w-5 h-5 shrink-0') %>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="flex flex-col md:flex-row gap-2 mt-5">
|
<div class="flex flex-col md:flex-row gap-2 mt-5">
|
||||||
<div class="form-control w-full">
|
<div class="form-control w-full">
|
||||||
<div id="status" class="radio-select grid grid-cols-2 gap-2 px-1">
|
<div id="status" class="radio-select grid grid-cols-2 gap-2 px-1">
|
||||||
<% ['', 'pending', 'completed', 'partially_completed', 'declined', 'expired'].each do |status| %>
|
<% ['', 'pending', 'completed', 'partially_completed', 'sent', 'opened', 'declined', 'expired'].each do |status| %>
|
||||||
<label class="radio-label cursor-pointer inline-flex items-center space-x-2">
|
<label class="radio-label cursor-pointer inline-flex items-center space-x-2">
|
||||||
<%= radio_button_tag 'status', status, params[:status] == status || (status == '' && params[:status].blank?), class: 'base-radio' %>
|
<%= radio_button_tag 'status', status, params[:status] == status || (status == '' && params[:status].blank?), class: 'base-radio' %>
|
||||||
<span><%= t(status.presence || 'all') %></span>
|
<span><%= t(status.presence || 'all') %></span>
|
||||||
|
|||||||
@@ -2,11 +2,9 @@
|
|||||||
<div class="max-w-md mx-auto px-2 mt-12 mb-4">
|
<div class="max-w-md mx-auto px-2 mt-12 mb-4">
|
||||||
<div class="space-y-6 mx-auto">
|
<div class="space-y-6 mx-auto">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<% if Docuseal.multitenant? %>
|
<div class="flex items-center justify-center">
|
||||||
<div class="flex items-center justify-center">
|
<%= render 'start_form/banner' %>
|
||||||
<%= render 'start_form/docuseal_logo' %>
|
</div>
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
|
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="mr-3">
|
<div class="mr-3">
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<%= render partial: 'submissions/annotation', collection: annots, as: :annot %>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<% 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_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 %>
|
<% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %>
|
||||||
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
||||||
<% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
<% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
||||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-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-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>
|
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-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-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-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>
|
||||||
@@ -47,8 +47,8 @@
|
|||||||
<div class="relative my-4 shadow-md">
|
<div class="relative my-4 shadow-md">
|
||||||
<img loading="lazy" src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>">
|
<img loading="lazy" src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>">
|
||||||
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
|
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
|
||||||
<% document_annots_index[index]&.each do |annot| %>
|
<% if annots = document_annots_index[index] %>
|
||||||
<%= render 'submissions/annotation', annot: %>
|
<%= render 'submit_form/annotations', annots: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
|
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
|
||||||
<% value = values[field['uuid']].presence || (field['default_value'].present? ? Submitters::SubmitValues.template_default_value_for_submitter(field['default_value'], @submitter.submission.submitters.find { |e| e.uuid == field['submitter_uuid'] }, with_time: false) : nil) %>
|
<% value = values[field['uuid']].presence || (field['default_value'].present? ? Submitters::SubmitValues.template_default_value_for_submitter(field['default_value'], @submitter.submission.submitters.find { |e| e.uuid == field['submitter_uuid'] }, with_time: false) : nil) %>
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
<% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %>
|
<% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %>
|
||||||
<% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<%= render 'submissions/value', area:, field:, attachments_index: @attachments_index, value:, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id] %>
|
<%= render 'submissions/value', area:, field:, attachments_index: @attachments_index, value: field.dig('preferences', 'mask').present? ? TextUtils.mask_value(value, field.dig('preferences', 'mask')) : value, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id] %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</submitter-item>
|
</submitter-item>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<%= render 'submissions/send_email', f:, template: @submitter.template, submitter: @submitter, resend_email: @submitter.sent_at?, submitter_email_message: @submitter_email_message, disable_save_as_default_template_option: true %>
|
<%= render 'submissions/send_email', f:, template: @submitter.template, submitter: @submitter, resend_email: @submitter.sent_at?, submitter_email_message: @submitter_email_message, disable_save_as_default_template_option: true, message_per_submitter: false %>
|
||||||
<%= render 'submissions/send_sms', f:, resend_sms: @submitter.sent_at? %>
|
<%= render 'submissions/send_sms', f:, resend_sms: @submitter.sent_at? %>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control mt-4">
|
<div class="form-control mt-4">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
||||||
<div class="flex justify-between items-center w-full mb-4">
|
<div class="flex justify-between items-center w-full mb-4">
|
||||||
<div class="flex items-center flex-grow min-w-0">
|
<div class="flex items-center flex-grow min-w-0">
|
||||||
<% if has_archived || @pagy.count > 0 %>
|
<% if has_archived || @pagy.count > 0 || @template_folders.present? %>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<%= render 'dashboard/toggle_view', selected: 'templates' %>
|
<%= render 'dashboard/toggle_view', selected: 'templates' %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -83,21 +83,71 @@
|
|||||||
<div class="collapse-content">
|
<div class="collapse-content">
|
||||||
<%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-1' }, data: { close_on_submit: false } do |f| %>
|
<%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-1' }, data: { close_on_submit: false } do |f| %>
|
||||||
<toggle-on-submit data-element-id="email_saved_alert1"></toggle-on-submit>
|
<toggle-on-submit data-element-id="email_saved_alert1"></toggle-on-submit>
|
||||||
<%= f.fields_for :preferences, Struct.new(:request_email_subject, :request_email_body).new(*(@template.preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY).value.values_at('subject', 'body'))) do |ff| %>
|
<%= tag.input id: 'request_email_per_submitter', value: '1', name: 'request_email_per_submitter', class: 'peer', type: 'checkbox', hidden: true, checked: @template.preferences['submitters'].to_a.size > 1 %>
|
||||||
<div class="form-control">
|
<div class="peer-checked:hidden">
|
||||||
<%= ff.label :request_email_subject, t('email_subject'), class: 'label' %>
|
<%= f.fields_for :preferences, Struct.new(:request_email_subject, :request_email_body).new(*(@template.preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY).value.values_at('subject', 'body'))) do |ff| %>
|
||||||
<%= ff.text_field :request_email_subject, required: true, class: 'base-input', dir: 'auto' %>
|
<div class="form-control">
|
||||||
</div>
|
<div class="flex justify-between">
|
||||||
<div class="form-control">
|
<%= ff.label :request_email_subject, t('email_subject'), class: 'label' %>
|
||||||
<div class="flex items-center">
|
<% if @template.submitters.size > 1 && @template.submitters.size < 5 %>
|
||||||
<%= ff.label :request_email_body, t('email_body'), class: 'label' %>
|
<label for="request_email_per_submitter" class="label underline">
|
||||||
<span class="tooltip tooltip-right" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY].call['body'].scan(/{.*?}/).join(', ') %>">
|
<%= t('edit_per_party') %>
|
||||||
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
|
</label>
|
||||||
</span>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<%= ff.text_field :request_email_subject, required: true, class: 'base-input', dir: 'auto' %>
|
||||||
</div>
|
</div>
|
||||||
<autoresize-textarea>
|
<div class="form-control">
|
||||||
<%= ff.text_area :request_email_body, required: true, class: 'base-input w-full py-2', dir: 'auto' %>
|
<div class="flex items-center">
|
||||||
</autoresize-textarea>
|
<%= ff.label :request_email_body, t('email_body'), class: 'label' %>
|
||||||
|
<span class="tooltip tooltip-right" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY].call['body'].scan(/{.*?}/).join(', ') %>">
|
||||||
|
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<autoresize-textarea>
|
||||||
|
<%= ff.text_area :request_email_body, required: true, class: 'base-input w-full py-2', dir: 'auto' %>
|
||||||
|
</autoresize-textarea>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% if @template.submitters.size > 1 && @template.submitters.size < 5 %>
|
||||||
|
<div class="hidden peer-checked:block">
|
||||||
|
<% options = @template.submitters.map { |e| [e['name'], "request_email_#{e['uuid']}"] } %>
|
||||||
|
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="flex relative px-1">
|
||||||
|
<ul class="tabs w-full flex flex-nowrap mb-2">
|
||||||
|
<% options.each_with_index do |(label, val), index| %>
|
||||||
|
<div class="w-full">
|
||||||
|
<%= f.radio_button :selected, val, checked: index.zero?, id: "#{val}_radio", data: { action: 'click:toggle-visible#trigger' }, class: 'hidden peer' %>
|
||||||
|
<%= f.label :selected, label, value: val, for: "#{val}_radio", class: 'tab w-full tab-lifted peer-checked:tab-active' %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</toggle-visible>
|
||||||
|
<%= f.fields_for :preferences do |ff| %>
|
||||||
|
<% @template.submitters.each_with_index do |submitter, index| %>
|
||||||
|
<div id="request_email_<%= submitter['uuid'] %>" class="<%= 'hidden' if index != 0 %>">
|
||||||
|
<% submitter_preferences = f.object.preferences['submitters'].to_a.find { |e| e['uuid'] == submitter['uuid'] } || {} %>
|
||||||
|
<%= ff.fields_for :submitters, Struct.new(:request_email_subject, :request_email_body).new(*(submitter_preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || @template.preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY).value.values_at('subject', 'body'))), index: nil do |fff| %>
|
||||||
|
<%= fff.hidden_field :uuid, value: submitter['uuid'] %>
|
||||||
|
<div class="form-control">
|
||||||
|
<%= fff.label :request_email_subject, t('email_subject'), class: 'label' %>
|
||||||
|
<%= fff.text_field :request_email_subject, required: true, class: 'base-input', dir: 'auto' %>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<%= fff.label :request_email_body, t('email_body'), class: 'label' %>
|
||||||
|
<span class="tooltip tooltip-right" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES[AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY].call['body'].scan(/{.*?}/).join(', ') %>">
|
||||||
|
<%= svg_icon('info_circle', class: 'w-4 h-4') %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<autoresize-textarea>
|
||||||
|
<%= fff.text_area :request_email_body, required: true, class: 'base-input w-full py-2', dir: 'auto' %>
|
||||||
|
</autoresize-textarea>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="form-control pt-2">
|
<div class="form-control pt-2">
|
||||||
@@ -235,12 +285,12 @@
|
|||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= ff.text_field :name, class: 'w-full outline-none border-transparent focus:border-transparent focus:ring-0 bg-base-100 px-1 peer mb-2', autocomplete: 'off', placeholder: "#{index + 1}#{(index + 1).ordinal} Party", required: true %>
|
<%= ff.text_field :name, class: 'w-full outline-none border-transparent focus:border-transparent focus:ring-0 bg-base-100 px-1 peer mb-2', autocomplete: 'off', placeholder: "#{index + 1}#{(index + 1).ordinal} Party", required: true %>
|
||||||
<% if @template.submitters.size == 2 %>
|
<% if @template.submitters.size == 2 %>
|
||||||
<%= ff.email_field :email, class: 'base-input', autocomplete: 'off', placeholder: t('default_email'), disabled: ff.object.is_requester || ff.object.invite_by_uuid.present? || ff.object.optional_invite_by_uuid.present?, id: field_uuid = SecureRandom.uuid %>
|
<%= tag.input name: ff.field_name(:email), value: ff.object.email, type: :email, class: 'base-input', multiple: true, autocomplete: 'off', placeholder: t('default_email'), disabled: ff.object.is_requester || ff.object.invite_by_uuid.present? || ff.object.optional_invite_by_uuid.present?, id: field_uuid = SecureRandom.uuid %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<toggle-attribute data-target-id="<%= email_field_uuid = SecureRandom.uuid %>" data-class-name="hidden" data-value="email">
|
<toggle-attribute data-target-id="<%= email_field_uuid = SecureRandom.uuid %>" data-class-name="hidden" data-value="email">
|
||||||
<%= ff.select :option, [[t('not_specified'), 'not_set'], [t('submission_requester'), 'is_requester'], [t('specified_email'), 'email'], *(@template.submitters - [submitter]).flat_map { |e| [[t('invite_by_name', name: e['name']), "invite_by_#{e['uuid']}"], [t('invite_by_name', name: e['name']) + " (#{t(:optional).capitalize})", "optional_invite_by_#{e['uuid']}"]] }, *(@template.submitters - [submitter]).map { |e| [t('same_as_name', name: e['name']), "linked_to_#{e['uuid']}"] }], {}, class: 'base-select mb-3' %>
|
<%= ff.select :option, [[t('not_specified'), 'not_set'], [t('submission_requester'), 'is_requester'], [t('specified_email'), 'email'], *(@template.submitters - [submitter]).flat_map { |e| [[t('invite_by_name', name: e['name']), "invite_by_#{e['uuid']}"], [t('invite_by_name', name: e['name']) + " (#{t(:optional).capitalize})", "optional_invite_by_#{e['uuid']}"]] }, *(@template.submitters - [submitter]).map { |e| [t('same_as_name', name: e['name']), "linked_to_#{e['uuid']}"] }], {}, class: 'base-select mb-3' %>
|
||||||
</toggle-attribute>
|
</toggle-attribute>
|
||||||
<%= ff.email_field :email, class: "base-input #{'hidden' if item.option != 'email'}", autocomplete: 'off', placeholder: t('default_email'), id: email_field_uuid %>
|
<%= tag.input name: ff.field_name(:email), type: :email, value: ff.object.email, multiple: true, class: "base-input #{'hidden' if item.option != 'email'}", autocomplete: 'off', placeholder: t('default_email'), id: email_field_uuid %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% if @template.submitters.size == 2 %>
|
<% if @template.submitters.size == 2 %>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= user.last_sign_in_at ? l(user.last_sign_in_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) : '-' %>
|
<%= user.current_sign_in_at ? l(user.current_sign_in_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) : '-' %>
|
||||||
</td>
|
</td>
|
||||||
<td class="flex items-center space-x-2 justify-end">
|
<td class="flex items-center space-x-2 justify-end">
|
||||||
<% if params[:status].blank? && can?(:update, user) && user.archived_at.blank? %>
|
<% if params[:status].blank? && can?(:update, user) && user.archived_at.blank? %>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"ignored_warnings": [
|
||||||
|
{
|
||||||
|
"fingerprint": "25f4ce5fee1e1180fa1919dc4ee78db3ab3457a956e4679503aa745771a43836",
|
||||||
|
"note": "Permitted parameters are necessary for creating submitters via API"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ Rails.application.configure do
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.active_storage.resolve_model_to_route = :rails_storage_proxy if ENV['ACTIVE_STORAGE_PUBLIC'] != 'true'
|
config.active_storage.resolve_model_to_route = :rails_storage_proxy if ENV['ACTIVE_STORAGE_PUBLIC'] != 'true'
|
||||||
config.active_storage.service_urls_expire_in = 4.hours
|
config.active_storage.service_urls_expire_in = ENV.fetch('PRESIGNED_URLS_EXPIRE_MINUTES', '240').to_i.minutes
|
||||||
|
|
||||||
# Mount Action Cable outside main process or domain.
|
# Mount Action Cable outside main process or domain.
|
||||||
# config.action_cable.mount_path = nil
|
# config.action_cable.mount_path = nil
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ Devise.setup do |config|
|
|||||||
|
|
||||||
# ==> Configuration for :rememberable
|
# ==> Configuration for :rememberable
|
||||||
# The time the user will be remembered without asking for credentials again.
|
# The time the user will be remembered without asking for credentials again.
|
||||||
config.remember_for = 2.years
|
config.remember_for = ENV.fetch('SESSION_REMEMBER_DAYS', '730').to_i.days
|
||||||
|
|
||||||
# Invalidates all the remember me tokens when the user signs out.
|
# Invalidates all the remember me tokens when the user signs out.
|
||||||
config.expire_all_remember_me_on_sign_out = true
|
config.expire_all_remember_me_on_sign_out = true
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ en: &en
|
|||||||
language_ko: 한국어
|
language_ko: 한국어
|
||||||
hi_there: Hi there
|
hi_there: Hi there
|
||||||
thanks: Thanks
|
thanks: Thanks
|
||||||
|
edit_per_party: Edit per party
|
||||||
reply_to: Reply to
|
reply_to: Reply to
|
||||||
pending_by_me: Pending by me
|
pending_by_me: Pending by me
|
||||||
partially_completed: Partially completed
|
partially_completed: Partially completed
|
||||||
@@ -42,6 +43,7 @@ en: &en
|
|||||||
hello_name: Hello %{name}
|
hello_name: Hello %{name}
|
||||||
you_are_invited_to_product_name: You are invited to %{product_name}
|
you_are_invited_to_product_name: You are invited to %{product_name}
|
||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'You have been invited to %{account_name} %{product_name}. Please sign up using the link below:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'You have been invited to %{account_name} %{product_name}. Please sign up using the link below:'
|
||||||
|
sent_using_product_name_in_testing_mode_html: 'Sent using <a href="%{product_url}">%{product_name}</a> in testing mode'
|
||||||
sent_using_product_name_free_document_signing_html: 'Sent using <a href="%{product_url}">%{product_name}</a> free document signing.'
|
sent_using_product_name_free_document_signing_html: 'Sent using <a href="%{product_url}">%{product_name}</a> free document signing.'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Sign documents with trusted certificate provided by DocuSeal. Your documents and data are never shared with DocuSeal. PDF checksum is provided to generate a trusted signature.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Sign documents with trusted certificate provided by DocuSeal. Your documents and data are never shared with DocuSeal. PDF checksum is provided to generate a trusted signature.
|
||||||
you_have_been_invited_to_submit_the_name_form: 'You have been invited to submit the "%{name}" form.'
|
you_have_been_invited_to_submit_the_name_form: 'You have been invited to submit the "%{name}" form.'
|
||||||
@@ -690,6 +692,21 @@ en: &en
|
|||||||
extremely_satisfied: Extremely Satisfied
|
extremely_satisfied: Extremely Satisfied
|
||||||
your_pro_plan_payment_is_overdue: Your Pro plan payment is overdue.
|
your_pro_plan_payment_is_overdue: Your Pro plan payment is overdue.
|
||||||
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: <a href="%{url}" class="link">Click here</a> to update your payment details and clear the invoice to ensure uninterrupted service.
|
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: <a href="%{url}" class="link">Click here</a> to update your payment details and clear the invoice to ensure uninterrupted service.
|
||||||
|
overdue_payment: Overdue Payment
|
||||||
|
your_pro_plan_has_been_suspended_due_to_unpaid_invoices_you_can_update_your_payment_details_to_settle_the_invoice_and_continue_using_docuseal_or_cancel_your_subscription: Your Pro Plan has been suspended due to unpaid invoices. You can update your payment details to settle the invoice and continue using DocuSeal or cancel your subscription.
|
||||||
|
manage_subscription: Manage Subscription
|
||||||
|
submission_created_by_email_html: '<b>Submission created</b> by %{email}'
|
||||||
|
submission_created_by_email_via_source_html: '<b>Submission created</b> by %{email} via %{source}'
|
||||||
|
submission_created_via_source_html: '<b>Submission created</b> via %{source}'
|
||||||
|
pro_user_seats_used: Pro user seats used
|
||||||
|
manage_plan: Manage plan
|
||||||
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: This submission has multiple signers, which prevents the use of a sharing link as it's unclear which signer is responsible for specific fields. To resolve this, follow this <a href="https://www.docuseal.com/resources/pre-filling-recipients" class="link font-bold" rel="noopener noreferrer nofollow" target="_blank">guide</a> to define the default signer details.
|
||||||
|
submission_sources:
|
||||||
|
api: API
|
||||||
|
bulk: Bulk Send
|
||||||
|
embed: Embedding
|
||||||
|
invie: Invite
|
||||||
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>Email sent</b> to %{submitter_name}'
|
send_email_to_html: '<b>Email sent</b> to %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>Reminder email sent</b> to %{submitter_name}'
|
send_reminder_email_to_html: '<b>Reminder email sent</b> to %{submitter_name}'
|
||||||
@@ -728,6 +745,7 @@ en: &en
|
|||||||
read: Read your data
|
read: Read your data
|
||||||
|
|
||||||
es: &es
|
es: &es
|
||||||
|
edit_per_party: Editar por parte
|
||||||
signed: Firmado
|
signed: Firmado
|
||||||
reply_to: Responder a
|
reply_to: Responder a
|
||||||
partially_completed: Parcialmente completado
|
partially_completed: Parcialmente completado
|
||||||
@@ -752,6 +770,7 @@ es: &es
|
|||||||
hello_name: Hola %{name}
|
hello_name: Hola %{name}
|
||||||
you_are_invited_to_product_name: Estás invitado a %{product_name}
|
you_are_invited_to_product_name: Estás invitado a %{product_name}
|
||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Has sido invitado a %{account_name} %{product_name}. Por favor, regístrate usando el enlace a continuación:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Has sido invitado a %{account_name} %{product_name}. Por favor, regístrate usando el enlace a continuación:'
|
||||||
|
sent_using_product_name_in_testing_mode_html: 'Enviado usando <a href="%{product_url}">%{product_name}</a> en Modo de Prueba'
|
||||||
sent_using_product_name_free_document_signing_html: 'Enviado usando la firma de documentos gratuita de <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Enviado usando la firma de documentos gratuita de <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Firme documentos con un certificado de confianza proporcionado por DocuSeal. Sus documentos y datos nunca se comparten con DocuSeal. Se proporciona un checksum de PDF para generar una firma de confianza.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Firme documentos con un certificado de confianza proporcionado por DocuSeal. Sus documentos y datos nunca se comparten con DocuSeal. Se proporciona un checksum de PDF para generar una firma de confianza.
|
||||||
hi_there: Hola
|
hi_there: Hola
|
||||||
@@ -1400,6 +1419,21 @@ es: &es
|
|||||||
extremely_satisfied: Extremadamente satisfecho
|
extremely_satisfied: Extremadamente satisfecho
|
||||||
your_pro_plan_payment_is_overdue: El pago de tu plan Pro está atrasado.
|
your_pro_plan_payment_is_overdue: El pago de tu plan Pro está atrasado.
|
||||||
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Haz clic aquí</a> para actualizar tus datos de pago y liquidar la factura para garantizar un servicio ininterrumpido.'
|
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Haz clic aquí</a> para actualizar tus datos de pago y liquidar la factura para garantizar un servicio ininterrumpido.'
|
||||||
|
overdue_payment: Pago Atrasado
|
||||||
|
your_pro_plan_has_been_suspended_due_to_unpaid_invoices_you_can_update_your_payment_details_to_settle_the_invoice_and_continue_using_docuseal_or_cancel_your_subscription: Tu plan Pro ha sido suspendido debido a facturas impagas. Puedes actualizar tus datos de pago para liquidar la factura y seguir usando DocuSeal o cancelar tu suscripción.
|
||||||
|
manage_subscription: Gestionar Suscripción
|
||||||
|
submission_created_by_email_html: '<b>Envío creado</b> por %{email}'
|
||||||
|
submission_created_by_email_via_source_html: '<b>Envío creado</b> por %{email} a través de %{source}'
|
||||||
|
submission_created_via_source_html: '<b>Envío creado</b> a través de %{source}'
|
||||||
|
pro_user_seats_used: Plazas de usuario Pro en uso
|
||||||
|
manage_plan: Gestionar plan
|
||||||
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: 'Este envío tiene múltiples firmantes, lo que impide el uso de un enlace para compartir, ya que no está claro qué firmante es responsable de los campos específicos. Para resolver esto, sigue esta <a href="https://www.docuseal.com/resources/pre-filling-recipients" class="link font-bold" rel="noopener noreferrer nofollow" target="_blank">guía</a> para definir los detalles predeterminados del firmante.'
|
||||||
|
submission_sources:
|
||||||
|
api: API
|
||||||
|
bulk: Envío masivo
|
||||||
|
embed: Integración
|
||||||
|
invite: Invitación
|
||||||
|
link: Enlace
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>Correo electrónico enviado</b> a %{submitter_name}'
|
send_email_to_html: '<b>Correo electrónico enviado</b> a %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>Correo de recordatorio enviado</b> a %{submitter_name}'
|
send_reminder_email_to_html: '<b>Correo de recordatorio enviado</b> a %{submitter_name}'
|
||||||
@@ -1438,6 +1472,7 @@ es: &es
|
|||||||
read: Leer tus datos
|
read: Leer tus datos
|
||||||
|
|
||||||
it: &it
|
it: &it
|
||||||
|
edit_per_party: Modifica per partito
|
||||||
signed: Firmato
|
signed: Firmato
|
||||||
reply_to: Rispondi a
|
reply_to: Rispondi a
|
||||||
pending_by_me: In sospeso da me
|
pending_by_me: In sospeso da me
|
||||||
@@ -1461,6 +1496,7 @@ it: &it
|
|||||||
hello_name: Ciao %{name}
|
hello_name: Ciao %{name}
|
||||||
you_are_invited_to_product_name: Sei stato invitato a %{product_name}
|
you_are_invited_to_product_name: Sei stato invitato a %{product_name}
|
||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sei stato invitato a %{account_name} %{product_name}. Registrati utilizzando il link qui sotto:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sei stato invitato a %{account_name} %{product_name}. Registrati utilizzando il link qui sotto:'
|
||||||
|
sent_using_product_name_in_testing_mode_html: 'Inviato utilizzando <a href="%{product_url}">%{product_name}</a> in Modalità di Test'
|
||||||
sent_using_product_name_free_document_signing_html: 'Inviato utilizzando la firma di documenti gratuita di <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Inviato utilizzando la firma di documenti gratuita di <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: "Firma documenti con un certificato di fiducia fornito da DocuSeal. I tuoi documenti e i tuoi dati non vengono mai condivisi con DocuSeal. Il checksum PDF è fornito per generare una firma di fiducia."
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: "Firma documenti con un certificato di fiducia fornito da DocuSeal. I tuoi documenti e i tuoi dati non vengono mai condivisi con DocuSeal. Il checksum PDF è fornito per generare una firma di fiducia."
|
||||||
hi_there: Ciao
|
hi_there: Ciao
|
||||||
@@ -2109,6 +2145,21 @@ it: &it
|
|||||||
extremely_satisfied: Estremamente soddisfatto
|
extremely_satisfied: Estremamente soddisfatto
|
||||||
your_pro_plan_payment_is_overdue: Il pagamento del tuo piano Pro è in ritardo.
|
your_pro_plan_payment_is_overdue: Il pagamento del tuo piano Pro è in ritardo.
|
||||||
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Fai clic qui</a> per aggiornare i tuoi dati di pagamento e saldare la fattura per garantire un servizio ininterrotto.'
|
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Fai clic qui</a> per aggiornare i tuoi dati di pagamento e saldare la fattura per garantire un servizio ininterrotto.'
|
||||||
|
overdue_payment: Pagamento Scaduto
|
||||||
|
your_pro_plan_has_been_suspended_due_to_unpaid_invoices_you_can_update_your_payment_details_to_settle_the_invoice_and_continue_using_docuseal_or_cancel_your_subscription: Il tuo piano Pro è stato sospeso a causa di fatture non pagate. Puoi aggiornare i tuoi dati di pagamento per saldare la fattura e continuare a utilizzare DocuSeal o annullare l'abbonamento.
|
||||||
|
manage_subscription: Gestisci Abbonamento
|
||||||
|
submission_created_by_email_html: '<b>Invio creato</b> da %{email}'
|
||||||
|
submission_created_by_email_via_source_html: '<b>Invio creato</b> da %{email} tramite %{source}'
|
||||||
|
submission_created_via_source_html: '<b>Invio creato</b> tramite %{source}'
|
||||||
|
pro_user_seats_used: Posti utente Pro in uso
|
||||||
|
manage_plan: Gestisci piano
|
||||||
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: "Questa sottomissione ha più firmatari, il che impedisce l'uso di un link di condivisione poiché non è chiaro quale firmatario sia responsabile di specifici campi. Per risolvere questo problema, segui questa <a href=\"https://www.docuseal.com/resources/pre-filling-recipients\" class=\"link font-bold\" rel=\"noopener noreferrer nofollow\" target=\"_blank\">guida</a> per definire i dettagli predefiniti del firmatario."
|
||||||
|
submission_sources:
|
||||||
|
api: API
|
||||||
|
bulk: Invio massivo
|
||||||
|
embed: Incorporamento
|
||||||
|
invite: Invito
|
||||||
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-mail inviato</b> a %{submitter_name}'
|
send_email_to_html: '<b>E-mail inviato</b> a %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>E-mail di promemoria inviato</b> a %{submitter_name}'
|
send_reminder_email_to_html: '<b>E-mail di promemoria inviato</b> a %{submitter_name}'
|
||||||
@@ -2147,6 +2198,7 @@ it: &it
|
|||||||
read: Leggi i tuoi dati
|
read: Leggi i tuoi dati
|
||||||
|
|
||||||
fr: &fr
|
fr: &fr
|
||||||
|
edit_per_party: Éditer par partie
|
||||||
signed: Signé
|
signed: Signé
|
||||||
reply_to: Répondre à
|
reply_to: Répondre à
|
||||||
partially_completed: Partiellement complété
|
partially_completed: Partiellement complété
|
||||||
@@ -2171,6 +2223,7 @@ fr: &fr
|
|||||||
hello_name: Bonjour %{name}
|
hello_name: Bonjour %{name}
|
||||||
you_are_invited_to_product_name: Vous êtes invité à %{product_name}
|
you_are_invited_to_product_name: Vous êtes invité à %{product_name}
|
||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Vous avez été invité à %{account_name} %{product_name}. Veuillez vous inscrire en utilisant le lien ci-dessous:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Vous avez été invité à %{account_name} %{product_name}. Veuillez vous inscrire en utilisant le lien ci-dessous:'
|
||||||
|
sent_using_product_name_in_testing_mode_html: 'Envoyé en utilisant <a href="%{product_url}">%{product_name}</a> en Mode Test'
|
||||||
sent_using_product_name_free_document_signing_html: 'Envoyé en utilisant la signature de documents gratuite de <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Envoyé en utilisant la signature de documents gratuite de <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Signez des documents avec un certificat de confiance fourni par DocuSeal. Vos documents et données ne sont jamais partagés avec DocuSeal. Un checksum PDF est fourni pour générer une signature de confiance.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Signez des documents avec un certificat de confiance fourni par DocuSeal. Vos documents et données ne sont jamais partagés avec DocuSeal. Un checksum PDF est fourni pour générer une signature de confiance.
|
||||||
hi_there: Bonjour
|
hi_there: Bonjour
|
||||||
@@ -2820,6 +2873,21 @@ fr: &fr
|
|||||||
extremely_satisfied: Extrêmement satisfait
|
extremely_satisfied: Extrêmement satisfait
|
||||||
your_pro_plan_payment_is_overdue: Le paiement de votre plan Pro est en retard.
|
your_pro_plan_payment_is_overdue: Le paiement de votre plan Pro est en retard.
|
||||||
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Cliquez ici</a> pour mettre à jour vos informations de paiement et régler la facture afin de garantir un service ininterrompu.'
|
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Cliquez ici</a> pour mettre à jour vos informations de paiement et régler la facture afin de garantir un service ininterrompu.'
|
||||||
|
overdue_payment: Paiement En Retard
|
||||||
|
your_pro_plan_has_been_suspended_due_to_unpaid_invoices_you_can_update_your_payment_details_to_settle_the_invoice_and_continue_using_docuseal_or_cancel_your_subscription: Votre plan Pro a été suspendu en raison de factures impayées. Vous pouvez mettre à jour vos informations de paiement pour régler la facture et continuer à utiliser DocuSeal ou annuler votre abonnement.
|
||||||
|
manage_subscription: Gérer l'Abonnement
|
||||||
|
submission_created_by_email_html: '<b>Soumission créée</b> par %{email}'
|
||||||
|
submission_created_by_email_via_source_html: '<b>Soumission créée</b> par %{email} via %{source}'
|
||||||
|
submission_created_via_source_html: '<b>Soumission créée</b> via %{source}'
|
||||||
|
pro_user_seats_used: Places utilisateur Pro en cours d'utilisation
|
||||||
|
manage_plan: Gérer le plan
|
||||||
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: 'Cette soumission comporte plusieurs signataires, ce qui empêche l’utilisation d’un lien de partage, car il n’est pas clair quel signataire est responsable de quels champs. Pour résoudre ce problème, suivez ce <a href="https://www.docuseal.com/resources/pre-filling-recipients" class="link font-bold" rel="noopener noreferrer nofollow" target="_blank">guide</a> pour définir les détails du signataire par défaut.'
|
||||||
|
submission_sources:
|
||||||
|
api: API
|
||||||
|
bulk: Envoi en masse
|
||||||
|
embed: Intégration
|
||||||
|
invite: Invitation
|
||||||
|
link: Lien
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-mail envoyé</b> à %{submitter_name}'
|
send_email_to_html: '<b>E-mail envoyé</b> à %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>E-mail de rappel envoyé</b> à %{submitter_name}'
|
send_reminder_email_to_html: '<b>E-mail de rappel envoyé</b> à %{submitter_name}'
|
||||||
@@ -2858,6 +2926,7 @@ fr: &fr
|
|||||||
read: Lire vos données
|
read: Lire vos données
|
||||||
|
|
||||||
pt: &pt
|
pt: &pt
|
||||||
|
edit_per_party: Edita por festa
|
||||||
signed: Assinado
|
signed: Assinado
|
||||||
reply_to: Responder a
|
reply_to: Responder a
|
||||||
partially_completed: Parcialmente concluído
|
partially_completed: Parcialmente concluído
|
||||||
@@ -2882,6 +2951,7 @@ pt: &pt
|
|||||||
hello_name: Olá %{name}
|
hello_name: Olá %{name}
|
||||||
you_are_invited_to_product_name: Você está convidado para %{product_name}
|
you_are_invited_to_product_name: Você está convidado para %{product_name}
|
||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Você foi convidado para %{account_name} %{product_name}. Inscreva-se usando o link abaixo:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Você foi convidado para %{account_name} %{product_name}. Inscreva-se usando o link abaixo:'
|
||||||
|
sent_using_product_name_in_testing_mode_html: 'Enviado usando <a href="%{product_url}">%{product_name}</a> no Modo de Teste'
|
||||||
sent_using_product_name_free_document_signing_html: 'Enviado usando a assinatura gratuita de documentos de <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Enviado usando a assinatura gratuita de documentos de <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Assine documentos com certificado confiável fornecido pela DocuSeal. Seus documentos e dados nunca são compartilhados com a DocuSeal. O checksum do PDF é fornecido para gerar uma assinatura confiável.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Assine documentos com certificado confiável fornecido pela DocuSeal. Seus documentos e dados nunca são compartilhados com a DocuSeal. O checksum do PDF é fornecido para gerar uma assinatura confiável.
|
||||||
hi_there: Olá
|
hi_there: Olá
|
||||||
@@ -3530,6 +3600,21 @@ pt: &pt
|
|||||||
extremely_satisfied: Extremamente satisfeito
|
extremely_satisfied: Extremamente satisfeito
|
||||||
your_pro_plan_payment_is_overdue: O pagamento do seu plano Pro está atrasado.
|
your_pro_plan_payment_is_overdue: O pagamento do seu plano Pro está atrasado.
|
||||||
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Clique aqui</a> para atualizar seus dados de pagamento e quitar a fatura para garantir um serviço ininterrupto.'
|
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Clique aqui</a> para atualizar seus dados de pagamento e quitar a fatura para garantir um serviço ininterrupto.'
|
||||||
|
overdue_payment: Pagamento Atrasado
|
||||||
|
your_pro_plan_has_been_suspended_due_to_unpaid_invoices_you_can_update_your_payment_details_to_settle_the_invoice_and_continue_using_docuseal_or_cancel_your_subscription: Seu plano Pro foi suspenso devido a faturas não pagas. Você pode atualizar seus dados de pagamento para quitar a fatura e continuar usando o DocuSeal ou cancelar sua assinatura.
|
||||||
|
manage_subscription: Gerenciar Assinatura
|
||||||
|
submission_created_by_email_html: '<b>Envio criado</b> por %{email}'
|
||||||
|
submission_created_by_email_via_source_html: '<b>Envio criado</b> por %{email} via %{source}'
|
||||||
|
submission_created_via_source_html: '<b>Envio criado</b> via %{source}'
|
||||||
|
pro_user_seats_used: Lugares de usuário Pro em uso
|
||||||
|
manage_plan: Gerenciar plano
|
||||||
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: 'Este envio tem vários signatários, o que impede o uso de um link de compartilhamento, pois não está claro qual signatário é responsável por quais campos. Para resolver isso, siga este <a href="https://www.docuseal.com/resources/pre-filling-recipients" class="link font-bold" rel="noopener noreferrer nofollow" target="_blank">guia</a> para definir os detalhes padrão do signatário.'
|
||||||
|
submission_sources:
|
||||||
|
api: API
|
||||||
|
bulk: Envio em massa
|
||||||
|
embed: Incorporação
|
||||||
|
invite: Convite
|
||||||
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-mail enviado</b> para %{submitter_name}'
|
send_email_to_html: '<b>E-mail enviado</b> para %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>E-mail de lembrete enviado</b> para %{submitter_name}'
|
send_reminder_email_to_html: '<b>E-mail de lembrete enviado</b> para %{submitter_name}'
|
||||||
@@ -3568,6 +3653,7 @@ pt: &pt
|
|||||||
read: Ler seus dados
|
read: Ler seus dados
|
||||||
|
|
||||||
de: &de
|
de: &de
|
||||||
|
edit_per_party: Bearbeiten pro Partei
|
||||||
signed: Unterschrieben
|
signed: Unterschrieben
|
||||||
reply_to: Antworten auf
|
reply_to: Antworten auf
|
||||||
partially_completed: Teilweise abgeschlossen
|
partially_completed: Teilweise abgeschlossen
|
||||||
@@ -3592,6 +3678,7 @@ de: &de
|
|||||||
hello_name: Hallo %{name}
|
hello_name: Hallo %{name}
|
||||||
you_are_invited_to_product_name: Sie sind eingeladen zu %{product_name}
|
you_are_invited_to_product_name: Sie sind eingeladen zu %{product_name}
|
||||||
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sie wurden zu %{account_name} %{product_name} eingeladen. Bitte registrieren Sie sich über den folgenden Link:'
|
you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_: 'Sie wurden zu %{account_name} %{product_name} eingeladen. Bitte registrieren Sie sich über den folgenden Link:'
|
||||||
|
sent_using_product_name_in_testing_mode_html: 'Gesendet über <a href="%{product_url}">%{product_name}</a> im Testmodus'
|
||||||
sent_using_product_name_free_document_signing_html: 'Gesendet mit der kostenlosen Dokumentensignierung von <a href="%{product_url}">%{product_name}</a>.'
|
sent_using_product_name_free_document_signing_html: 'Gesendet mit der kostenlosen Dokumentensignierung von <a href="%{product_url}">%{product_name}</a>.'
|
||||||
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Unterzeichnen Sie Dokumente mit einem vertrauenswürdigen Zertifikat von DocuSeal. Ihre Dokumente und Daten werden niemals mit DocuSeal geteilt. Eine PDF-Prüfziffer wird bereitgestellt, um eine vertrauenswürdige Signatur zu generieren.
|
sign_documents_with_trusted_certificate_provided_by_docu_seal_your_documents_and_data_are_never_shared_with_docu_seal_p_d_f_checksum_is_provided_to_generate_a_trusted_signature: Unterzeichnen Sie Dokumente mit einem vertrauenswürdigen Zertifikat von DocuSeal. Ihre Dokumente und Daten werden niemals mit DocuSeal geteilt. Eine PDF-Prüfziffer wird bereitgestellt, um eine vertrauenswürdige Signatur zu generieren.
|
||||||
hi_there: Hallo
|
hi_there: Hallo
|
||||||
@@ -4240,6 +4327,21 @@ de: &de
|
|||||||
extremely_satisfied: Extrem zufrieden
|
extremely_satisfied: Extrem zufrieden
|
||||||
your_pro_plan_payment_is_overdue: Ihre Zahlung für den Pro-Plan ist überfällig.
|
your_pro_plan_payment_is_overdue: Ihre Zahlung für den Pro-Plan ist überfällig.
|
||||||
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Klicken Sie hier</a>, um Ihre Zahlungsdaten zu aktualisieren und die Rechnung zu begleichen, um einen unterbrechungsfreien Service sicherzustellen.'
|
click_here_to_update_your_payment_details_and_clear_the_invoice_to_ensure_uninterrupted_service_html: '<a href="%{url}" class="link">Klicken Sie hier</a>, um Ihre Zahlungsdaten zu aktualisieren und die Rechnung zu begleichen, um einen unterbrechungsfreien Service sicherzustellen.'
|
||||||
|
overdue_payment: Überfällige Zahlung
|
||||||
|
your_pro_plan_has_been_suspended_due_to_unpaid_invoices_you_can_update_your_payment_details_to_settle_the_invoice_and_continue_using_docuseal_or_cancel_your_subscription: Ihr Pro-Plan wurde aufgrund unbezahlter Rechnungen ausgesetzt. Sie können Ihre Zahlungsdaten aktualisieren, um die Rechnung zu begleichen und DocuSeal weiterhin zu nutzen, oder Ihr Abonnement kündigen.
|
||||||
|
manage_subscription: Abonnement Verwalten
|
||||||
|
submission_created_by_email_html: '<b>Übermittlung erstellt</b> von %{email}'
|
||||||
|
submission_created_by_email_via_source_html: '<b>Übermittlung erstellt</b> durch %{email} über %{source}'
|
||||||
|
submission_created_via_source_html: '<b>Übermittlung erstellt</b> über %{source}'
|
||||||
|
pro_user_seats_used: Verwendete Pro-Benutzerplätze
|
||||||
|
manage_plan: Plan verwalten
|
||||||
|
this_submission_has_multiple_signers_which_prevents_the_use_of_a_sharing_link_html: 'Diese Übermittlung hat mehrere Unterzeichner, was die Nutzung eines Freigabelinks verhindert, da unklar ist, welcher Unterzeichner für welche Felder verantwortlich ist. Um dies zu lösen, folgen Sie dieser <a href="https://www.docuseal.com/resources/pre-filling-recipients" class="link font-bold" rel="noopener noreferrer nofollow" target="_blank">Anleitung</a>, um die Standarddetails des Unterzeichners festzulegen.'
|
||||||
|
submission_sources:
|
||||||
|
api: API
|
||||||
|
bulk: Massenversand
|
||||||
|
embed: Einbettung
|
||||||
|
invite: Einladung
|
||||||
|
link: Link
|
||||||
submission_event_names:
|
submission_event_names:
|
||||||
send_email_to_html: '<b>E-Mail gesendet</b> an %{submitter_name}'
|
send_email_to_html: '<b>E-Mail gesendet</b> an %{submitter_name}'
|
||||||
send_reminder_email_to_html: '<b>Erinnerungs-E-Mail gesendet</b> an %{submitter_name}'
|
send_reminder_email_to_html: '<b>Erinnerungs-E-Mail gesendet</b> an %{submitter_name}'
|
||||||
|
|||||||
@@ -33,8 +33,14 @@ environment ENV.fetch('RAILS_ENV', 'development')
|
|||||||
# the concurrency of the application would be max `threads` * `workers`.
|
# the concurrency of the application would be max `threads` * `workers`.
|
||||||
# Workers do not work on JRuby or Windows (both of which do not support
|
# Workers do not work on JRuby or Windows (both of which do not support
|
||||||
# processes).
|
# processes).
|
||||||
#
|
|
||||||
workers ENV.fetch('WEB_CONCURRENCY', 0)
|
if ENV['WEB_CONCURRENCY_AUTO'] == 'true'
|
||||||
|
require 'etc'
|
||||||
|
|
||||||
|
workers Etc.nprocessors
|
||||||
|
else
|
||||||
|
workers ENV.fetch('WEB_CONCURRENCY', 0)
|
||||||
|
end
|
||||||
|
|
||||||
# Use the `preload_app!` method when specifying a `workers` number.
|
# Use the `preload_app!` method when specifying a `workers` number.
|
||||||
# This directive tells Puma to first boot the application and load code
|
# This directive tells Puma to first boot the application and load code
|
||||||
|
|||||||