diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb
index b68f32ae..5257da04 100644
--- a/app/controllers/submissions_controller.rb
+++ b/app/controllers/submissions_controller.rb
@@ -19,6 +19,8 @@ class SubmissionsController < ApplicationController
def create
authorize!(:create, Submission)
+ save_template_message(@template, params) if params[:save_message] == '1'
+
if params[:is_custom_message] != '1'
params.delete(:subject)
params.delete(:body)
@@ -57,6 +59,13 @@ class SubmissionsController < ApplicationController
private
+ def save_template_message(template, params)
+ template.preferences['request_email_subject'] = params[:subject] if params[:subject].present?
+ template.preferences['request_email_body'] = params[:body] if params[:body].present?
+
+ template.save!
+ end
+
def submissions_params
params.permit(submission: { submitters: [%i[uuid email phone name]] })
end
diff --git a/app/controllers/templates_preferences_controller.rb b/app/controllers/templates_preferences_controller.rb
new file mode 100644
index 00000000..48737fc1
--- /dev/null
+++ b/app/controllers/templates_preferences_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class TemplatesPreferencesController < ApplicationController
+ load_and_authorize_resource :template
+
+ def show; end
+
+ def create
+ authorize!(:update, @template)
+
+ @template.preferences = @template.preferences.merge(template_params[:preferences])
+ @template.save!
+
+ head :ok
+ end
+
+ private
+
+ def template_params
+ params.require(:template).permit(preferences: %i[bcc_completed request_email_subject request_email_body])
+ end
+end
diff --git a/app/javascript/application.js b/app/javascript/application.js
index b28d0bb2..5056863d 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -23,6 +23,7 @@ import SignatureForm from './elements/signature_form'
import SubmitForm from './elements/submit_form'
import PromptPassword from './elements/prompt_password'
import EmailsTextarea from './elements/emails_textarea'
+import ToggleOnSubmit from './elements/toggle_on_submit'
import * as TurboInstantClick from './lib/turbo_instant_click'
@@ -58,6 +59,7 @@ window.customElements.define('submit-form', SubmitForm)
window.customElements.define('prompt-password', PromptPassword)
window.customElements.define('emails-textarea', EmailsTextarea)
window.customElements.define('toggle-cookies', ToggleCookies)
+window.customElements.define('toggle-on-submit', ToggleOnSubmit)
document.addEventListener('turbo:before-fetch-request', encodeMethodIntoRequestBody)
document.addEventListener('turbo:submit-end', async (event) => {
diff --git a/app/javascript/elements/toggle_on_submit.js b/app/javascript/elements/toggle_on_submit.js
new file mode 100644
index 00000000..8cbc213f
--- /dev/null
+++ b/app/javascript/elements/toggle_on_submit.js
@@ -0,0 +1,35 @@
+export default class extends HTMLElement {
+ connectedCallback () {
+ document.addEventListener('turbo:submit-end', this.onSubmitEnd)
+
+ this.form.addEventListener('submit', this.onSubmit)
+ }
+
+ disconnectedCallback () {
+ document.removeEventListener('turbo:submit-end', this.onSubmitEnd)
+
+ this.form.removeEventListener('submit', this.onSubmit)
+ }
+
+ onSubmit = () => {
+ this.element.classList.add('invisible')
+ }
+
+ onSubmitEnd = (event) => {
+ if (event.target === this.form) {
+ const resp = event.detail?.formSubmission?.result?.fetchResponse?.response
+
+ if (resp?.status / 100 === 2) {
+ this.element.classList.remove('invisible')
+ }
+ }
+ }
+
+ get element () {
+ return document.getElementById(this.dataset.elementId)
+ }
+
+ get form () {
+ return this.closest('form')
+ }
+}
diff --git a/app/jobs/process_submitter_completion_job.rb b/app/jobs/process_submitter_completion_job.rb
index cf22112a..1f2b740d 100644
--- a/app/jobs/process_submitter_completion_job.rb
+++ b/app/jobs/process_submitter_completion_job.rb
@@ -34,6 +34,7 @@ class ProcessSubmitterCompletionJob < ApplicationJob
end
bcc = submission.preferences['bcc_completed'].presence ||
+ submission.template.preferences['bcc_completed'].presence ||
submission.account.account_configs
.find_by(key: AccountConfig::BCC_EMAILS)&.value
diff --git a/app/mailers/submitter_mailer.rb b/app/mailers/submitter_mailer.rb
index 545aa226..5ad6a868 100644
--- a/app/mailers/submitter_mailer.rb
+++ b/app/mailers/submitter_mailer.rb
@@ -14,8 +14,8 @@ class SubmitterMailer < ApplicationMailer
@email_message = submitter.account.email_messages.find_by(uuid: submitter.preferences['email_message_uuid'])
end
- @body = @email_message&.body.presence
- @subject = @email_message&.subject.presence
+ @body = @email_message&.body.presence || @submitter.template.preferences['request_email_body'].presence
+ @subject = @email_message&.subject.presence || @submitter.template.preferences['request_email_subject'].presence
@email_config = AccountConfigs.find_for_account(@current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY)
diff --git a/app/models/template.rb b/app/models/template.rb
index 2e499030..bfa21fae 100644
--- a/app/models/template.rb
+++ b/app/models/template.rb
@@ -8,6 +8,7 @@
# archived_at :datetime
# fields :text not null
# name :string not null
+# preferences :text not null
# schema :text not null
# slug :string not null
# source :text not null
@@ -41,12 +42,14 @@ class Template < ApplicationRecord
before_validation :maybe_set_default_folder, on: :create
+ attribute :preferences, :string, default: -> { {} }
attribute :fields, :string, default: -> { [] }
attribute :schema, :string, default: -> { [] }
attribute :submitters, :string, default: -> { [{ name: DEFAULT_SUBMITTER_NAME, uuid: SecureRandom.uuid }] }
attribute :slug, :string, default: -> { SecureRandom.base58(14) }
attribute :source, :string, default: 'native'
+ serialize :preferences, coder: JSON
serialize :fields, coder: JSON
serialize :schema, coder: JSON
serialize :submitters, coder: JSON
diff --git a/app/views/icons/_adjustments_horizontal.html.erb b/app/views/icons/_adjustments_horizontal.html.erb
new file mode 100644
index 00000000..f7821c54
--- /dev/null
+++ b/app/views/icons/_adjustments_horizontal.html.erb
@@ -0,0 +1,12 @@
+
diff --git a/app/views/icons/_settings.html.erb b/app/views/icons/_settings.html.erb
new file mode 100644
index 00000000..54d301e7
--- /dev/null
+++ b/app/views/icons/_settings.html.erb
@@ -0,0 +1,5 @@
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 4dd73600..9f7f527b 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -12,6 +12,7 @@
+
<%= render 'shared/navbar' %>
<% if flash.present? %><%= render 'shared/flash' %><% end %>
diff --git a/app/views/shared/_turbo_drawer.html.erb b/app/views/shared/_turbo_drawer.html.erb
new file mode 100644
index 00000000..251b5136
--- /dev/null
+++ b/app/views/shared/_turbo_drawer.html.erb
@@ -0,0 +1,22 @@
+
+
+
+
+ <% if local_assigns[:title] %>
+
+
+ <%= local_assigns[:title] %>
+
+
×
+
+ <% else %>
+
+ ×
+
+ <% end %>
+
+ <%= yield %>
+
+
+
+
diff --git a/app/views/submissions/_send_email.html.erb b/app/views/submissions/_send_email.html.erb
index a537bcd4..04f352fa 100644
--- a/app/views/submissions/_send_email.html.erb
+++ b/app/views/submissions/_send_email.html.erb
@@ -35,7 +35,7 @@
diff --git a/app/views/templates/_embedding.html.erb b/app/views/templates/_embedding.html.erb
index af6df88c..8b9e30ed 100644
--- a/app/views/templates/_embedding.html.erb
+++ b/app/views/templates/_embedding.html.erb
@@ -1,4 +1,4 @@
-
+
@@ -173,3 +190,61 @@ export default {
+
+
+
+ <%= link_to 'Learn More', console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/embedding/form"), target: '_blank', data: { turbo: false }, class: 'btn btn-ghost text-gray-100 flex', rel: 'noopener' %>
+
+
+
+
+
+
import { Component } from '@angular/core';
+import { DocusealFormComponent } from '@docuseal/angular';
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [DocusealFormComponent],
+ template: `
+ <div class="app">
+ <docuseal-form
+ [src]="'<%= start_form_url(slug: template.slug) %>'">
+ </docuseal-form>
+ </div>
+ `
+})
+export class AppComponent {}
+
+
+
diff --git a/app/views/templates/_title.html.erb b/app/views/templates/_title.html.erb
index 8bf4c7fa..2394a143 100644
--- a/app/views/templates/_title.html.erb
+++ b/app/views/templates/_title.html.erb
@@ -38,10 +38,10 @@
<% if !template.archived_at? %>
<% if can?(:update, template) && (Docuseal.multitenant? || current_account.testing? || current_account.id == 1) %>
-
- <%= link_to template_code_modal_path(template), class: 'btn btn-ghost btn-sm flex-1 hidden md:flex', data: { turbo_frame: :modal } do %>
+
+ <%= link_to template_preferences_path(template), class: 'btn border border-base-200 bg-base-200 hover:bg-base-300 hover:border-base-300 btn-sm flex-1 hidden md:flex', data: { turbo_frame: :drawer } do %>
- <%= svg_icon('code', class: 'w-6 h-6') %>
+ <%= svg_icon('adjustments_horizontal', class: 'w-6 h-6') %>
<% end %>
diff --git a/app/views/templates_preferences/show.html.erb b/app/views/templates_preferences/show.html.erb
new file mode 100644
index 00000000..6d1861a1
--- /dev/null
+++ b/app/views/templates_preferences/show.html.erb
@@ -0,0 +1,100 @@
+<%= render 'shared/turbo_drawer', title: 'Preferences', close_after_submit: false do %>
+ <% options = [['General', 'general'], ['API and Embedding', 'api']] %>
+
+
+ <% options.each_with_index do |(label, value), index| %>
+
+ <%= radio_button_tag 'option', value, value == 'general', class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %>
+
+
+ <% end %>
+
+
+
+ <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-1' } do |f| %>
+
+ <%= 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.label :request_email_subject, 'Email subject', class: 'label' %>
+ <%= ff.text_field :request_email_subject, required: true, class: 'base-input', dir: 'auto' %>
+
+
+ <% end %>
+
+ <% end %>
+ <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-2' } do |f| %>
+
+ <%= f.fields_for :preferences, Struct.new(:bcc_completed).new(@template.preferences['bcc_completed']) do |ff| %>
+
+ <%= ff.label :bcc_completed, class: 'label' do %>
+
+
+ Completed documents BCC address
+
+
+ <% end %>
+ <%= ff.email_field :bcc_completed, autocomplete: 'off', class: 'base-input' %>
+
+ <% end %>
+
+ <% end %>
+ <%= render 'templates_code_modal/preferences' %>
+
+
+
+
+
+
+ <%= render 'shared/clipboard_copy', icon: 'copy', text: @template.id, class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %>
+
+
+
+
+
+
+ <%= render 'shared/clipboard_copy', icon: 'copy', text: start_form_url(slug: @template.slug), class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %>
+
+
+ <%= render 'templates_code_modal/placeholder' %>
+ <%= render 'templates/embedding', template: @template %>
+ <% if can?(:manage, TemplateSharing.new(template: @template)) %>
+ <%= form_for '', url: template_sharings_testing_index_path, method: :post, html: { class: 'mt-1' } do |f| %>
+ <%= f.hidden_field :template_id, value: @template.id %>
+
+
+ Share template with Test Environment
+
+ <%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: current_account.testing_accounts), onchange: 'this.form.requestSubmit()' %>
+
+ <% end %>
+
+
+ <% end %>
+
+<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index 237d6e84..b7ba10af 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -76,6 +76,7 @@ Rails.application.routes.draw do
resource :folder, only: %i[edit update], controller: 'templates_folders'
resource :preview, only: %i[show], controller: 'templates_preview'
resource :code_modal, only: %i[show], controller: 'templates_code_modal'
+ resource :preferences, only: %i[show create], controller: 'templates_preferences'
resources :submissions_export, only: %i[index new]
end
resources :preview_document_page, only: %i[show], path: '/preview/:signed_uuid'
diff --git a/db/migrate/20240416170023_add_preferences_to_templates.rb b/db/migrate/20240416170023_add_preferences_to_templates.rb
new file mode 100644
index 00000000..80241038
--- /dev/null
+++ b/db/migrate/20240416170023_add_preferences_to_templates.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddPreferencesToTemplates < ActiveRecord::Migration[7.1]
+ class MigrationTemplate < ApplicationRecord
+ self.table_name = 'templates'
+ end
+
+ def change
+ add_column :templates, :preferences, :text
+
+ MigrationTemplate.where(preferences: nil).update_all(preferences: '{}')
+
+ change_column_null :templates, :preferences, false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4804ce0b..6c663ed6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2024_04_05_165329) do
+ActiveRecord::Schema[7.1].define(version: 2024_04_16_170023) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -222,6 +222,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_05_165329) do
t.text "source", null: false
t.bigint "folder_id", null: false
t.string "external_id"
+ t.text "preferences", null: false
t.index ["account_id"], name: "index_templates_on_account_id"
t.index ["author_id"], name: "index_templates_on_author_id"
t.index ["folder_id"], name: "index_templates_on_folder_id"