diff --git a/.eslintrc b/.eslintrc index e7045bb5..dd91e7c3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,7 +8,8 @@ }, "rules": { "vue/no-deprecated-html-element-is": 0, - "vue/no-mutating-props": 0 + "vue/no-mutating-props": 0, + "vue/one-component-per-file": 0 }, "parserOptions": { "ecmaVersion": 2022, diff --git a/.rubocop.yml b/.rubocop.yml index 2a10dcf8..d538abe7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -44,7 +44,7 @@ Layout/LineLength: AllowedPatterns: ['\A\s*#'] Metrics/AbcSize: - Max: 40 + Max: 45 Metrics/ModuleLength: Max: 500 diff --git a/README.md b/README.md index b5a8b726..e3514f86 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ DocuSeal is an open source platform that provides secure and efficient digital d - Automated reminders - Invitation and identify verification via SMS - Conditional fields and formulas +- Bulk send with CSV, XLSX spreadsheet import - SSO / SAML - Template creation with HTML API ([Guide](https://www.docuseal.co/guides/create-pdf-document-fillable-form-with-html-api)) - Template creation with PDF or DOCX and field tags API ([Guide](https://www.docuseal.co/guides/use-embedded-text-field-tags-in-the-pdf-to-create-a-fillable-form)) diff --git a/app/javascript/application.js b/app/javascript/application.js index be54143b..fc2e95ce 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -3,6 +3,7 @@ import { encodeMethodIntoRequestBody } from '@hotwired/turbo-rails/app/javascrip import { createApp, reactive } from 'vue' import TemplateBuilder from './template_builder/builder' +import ImportList from './template_builder/import_list' import ToggleVisible from './elements/toggle_visible' import DisableHidden from './elements/disable_hidden' @@ -111,3 +112,23 @@ window.customElements.define('template-builder', class extends HTMLElement { this.appElem?.remove() } }) + +window.customElements.define('import-list', class extends HTMLElement { + connectedCallback () { + this.appElem = document.createElement('div') + + this.app = createApp(ImportList, { + template: JSON.parse(this.dataset.template), + authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content + }) + + this.app.mount(this.appElem) + + this.appendChild(this.appElem) + } + + disconnectedCallback () { + this.app?.unmount() + this.appElem?.remove() + } +}) diff --git a/app/javascript/application.scss b/app/javascript/application.scss index f0d6429a..26ad688a 100644 --- a/app/javascript/application.scss +++ b/app/javascript/application.scss @@ -83,6 +83,11 @@ button[disabled] .enabled { bottom: auto; } +.tooltip-pre:before { + white-space: pre; + text-align: left; +} + .tooltip-bottom-end:after { transform: translateX(-25%); border-color: transparent transparent var(--tooltip-color) transparent; diff --git a/app/javascript/template_builder/import_list.vue b/app/javascript/template_builder/import_list.vue new file mode 100644 index 00000000..39284929 --- /dev/null +++ b/app/javascript/template_builder/import_list.vue @@ -0,0 +1,383 @@ + + + diff --git a/app/views/submissions/_bulk_send_placeholder.html.erb b/app/views/submissions/_bulk_send_placeholder.html.erb new file mode 100644 index 00000000..acbafe3d --- /dev/null +++ b/app/views/submissions/_bulk_send_placeholder.html.erb @@ -0,0 +1,11 @@ +
+ <%= svg_icon('info_circle', class: 'w-6 h-6') %> +
+

Bulk send from Excel XLSX or CSV

+

+ Unlock with DocuSeal Pro +
+ " data-turbo="false">Learn More +

+
+
diff --git a/app/views/submissions/_list_form.html.erb b/app/views/submissions/_list_form.html.erb new file mode 100644 index 00000000..30358ba4 --- /dev/null +++ b/app/views/submissions/_list_form.html.erb @@ -0,0 +1 @@ +<%= render 'submissions/bulk_send_placeholder' %> diff --git a/app/views/submissions/new.html.erb b/app/views/submissions/new.html.erb index 9dccd553..fd08441c 100644 --- a/app/views/submissions/new.html.erb +++ b/app/views/submissions/new.html.erb @@ -1,5 +1,5 @@ <%= render 'shared/turbo_modal', title: params[:selfsign] ? 'Add Recipients' : 'Add New Recipients' do %> - <% options = [['via Email', 'email'], ['via Phone', 'phone'], %w[Detailed detailed], (Docuseal.multitenant? && params[:with_link] && @template.submitters.to_a.size < 2 ? ['via Link', 'link'] : nil)].compact %> + <% options = [['via Email', 'email'], ['via Phone', 'phone'], %w[Detailed detailed], ['Upload List', 'list']].compact %>
<% options.each_with_index do |(label, value), index| %> @@ -22,11 +22,9 @@ - <% if Docuseal.multitenant? && params[:with_link] && @template.submitters.to_a.size < 2 %> - - <% end %> +
<%= content_for(:modal_extra) %> <% end %> diff --git a/db/migrate/20231112224432_update_field_options.rb b/db/migrate/20231112224432_update_field_options.rb index b72db381..ea488e75 100644 --- a/db/migrate/20231112224432_update_field_options.rb +++ b/db/migrate/20231112224432_update_field_options.rb @@ -9,7 +9,6 @@ class UpdateFieldOptions < ActiveRecord::Migration[7.0] self.table_name = 'submissions' end - # rubocop:disable Metrics def up MigrationTemplate.find_each do |template| next if template.fields.blank? @@ -43,7 +42,6 @@ class UpdateFieldOptions < ActiveRecord::Migration[7.0] submission.update_columns(template_fields: new_fields.to_json) if template_fields != new_fields end end - # rubocop:enable Metrics def down nil diff --git a/lib/submissions/create_from_submitters.rb b/lib/submissions/create_from_submitters.rb index 191d9779..d4f4fd92 100644 --- a/lib/submissions/create_from_submitters.rb +++ b/lib/submissions/create_from_submitters.rb @@ -7,7 +7,7 @@ module Submissions def call(template:, user:, submissions_attrs:, source:, submitters_order:, mark_as_sent: false, params: {}) preferences = Submitters.normalize_preferences(user.account, user, params) - Array.wrap(submissions_attrs).map do |attrs| + Array.wrap(submissions_attrs).filter_map do |attrs| submission_preferences = Submitters.normalize_preferences(user.account, user, attrs) submission_preferences = preferences.merge(submission_preferences) @@ -36,6 +36,8 @@ module Submissions preferences: preferences.merge(submission_preferences)) end + next if submission.submitters.blank? + submission.tap(&:save!) end end diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index aa936814..e142b6bf 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -43,7 +43,7 @@ module Submissions template = submitter.submission.template - account = submitter.submission.template.account + account = submitter.account pkcs = Accounts.load_signing_pkcs(account) tsa_url = Accounts.load_timeserver_url(account) attachments_data_cache = {}