diff --git a/app/controllers/start_form_controller.rb b/app/controllers/start_form_controller.rb
index 35e0fbcc..080b9807 100644
--- a/app/controllers/start_form_controller.rb
+++ b/app/controllers/start_form_controller.rb
@@ -97,6 +97,7 @@ class StartFormController < ApplicationController
submitter.submission ||= Submission.new(template:,
account_id: template.account_id,
template_submitters: template.submitters,
+ expire_at: Templates.build_default_expire_at(template),
submitters: [submitter],
source: :link)
diff --git a/app/controllers/templates_preferences_controller.rb b/app/controllers/templates_preferences_controller.rb
index 2c9194b7..8f820e3f 100644
--- a/app/controllers/templates_preferences_controller.rb
+++ b/app/controllers/templates_preferences_controller.rb
@@ -34,6 +34,12 @@ class TemplatesPreferencesController < ApplicationController
submitters: [%i[uuid request_email_subject request_email_body]]]
).tap do |attrs|
attrs[:preferences].delete(:submitters) if params[:request_email_per_submitter] != '1'
+
+ if (default_expire_at = attrs.dig(:preferences, :default_expire_at).presence)
+ attrs[:preferences][:default_expire_at] =
+ (ActiveSupport::TimeZone[current_account.timezone] || Time.zone).parse(default_expire_at).utc
+ end
+
attrs[:preferences] = attrs[:preferences].transform_values do |value|
if %w[true false].include?(value)
value == 'true'
diff --git a/app/javascript/elements/dashboard_dropzone.js b/app/javascript/elements/dashboard_dropzone.js
index 19372c4c..d04a17d1 100644
--- a/app/javascript/elements/dashboard_dropzone.js
+++ b/app/javascript/elements/dashboard_dropzone.js
@@ -108,7 +108,6 @@ export default targetable(class extends HTMLElement {
redirect: 'manual',
body: formData,
headers: {
- Accept: 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
}
}).finally(() => {
diff --git a/app/javascript/template_builder/dropzone.vue b/app/javascript/template_builder/dropzone.vue
index 72705b99..ce97bcc1 100644
--- a/app/javascript/template_builder/dropzone.vue
+++ b/app/javascript/template_builder/dropzone.vue
@@ -23,13 +23,14 @@
{{ message }}
diff --git a/app/models/template.rb b/app/models/template.rb
index 366217fb..28441a9a 100644
--- a/app/models/template.rb
+++ b/app/models/template.rb
@@ -36,24 +36,6 @@
#
class Template < ApplicationRecord
DEFAULT_SUBMITTER_NAME = 'First Party'
- EXPIRATION_DURATIONS = {
- one_day: 1.day,
- two_days: 2.days,
- three_days: 3.days,
- four_days: 4.days,
- five_days: 5.days,
- six_days: 6.days,
- seven_days: 7.days,
- eight_days: 8.days,
- nine_days: 9.days,
- ten_days: 10.days,
- two_weeks: 14.days,
- three_weeks: 21.days,
- four_weeks: 28.days,
- one_month: 1.month,
- two_months: 2.months,
- three_months: 3.months
- }.with_indifferent_access.freeze
belongs_to :author, class_name: 'User'
belongs_to :account
diff --git a/app/views/icons/_clock_exclamation.html.erb b/app/views/icons/_clock_exclamation.html.erb
new file mode 100644
index 00000000..f6dc08ee
--- /dev/null
+++ b/app/views/icons/_clock_exclamation.html.erb
@@ -0,0 +1,3 @@
+
diff --git a/app/views/submissions/show.html.erb b/app/views/submissions/show.html.erb
index b027c0c8..75fe1ea7 100644
--- a/app/views/submissions/show.html.erb
+++ b/app/views/submissions/show.html.erb
@@ -172,12 +172,26 @@
<% end %>
- <%= svg_icon('writing', class: 'w-5 h-5') %>
+ <% if @submission.expire_at? && submitter && !submitter.completed_at? %>
+ <%= svg_icon('clock_exclamation', class: 'w-5 h-5') %>
+ <% else %>
+ <%= svg_icon('writing', class: 'w-5 h-5') %>
+ <% end %>
<% if submitter&.declined_at? %>
<%= t('declined_on_time', time: l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
<% elsif submitter %>
- <%= submitter.completed_at? ? l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) : t('not_completed_yet') %>
+ <% if submitter.completed_at? %>
+ <%= l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) %>
+ <% elsif @submission.expire_at? %>
+ <% if @submission.expired? %>
+ <%= t(:expired) %>
+ <% else %>
+ <%= t('expire_on_time', time: l(@submission.expire_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
+ <% end %>
+ <% else %>
+ <%= t('not_completed_yet') %>
+ <% end %>
<% else %>
<%= t('not_invited_yet') %>
<% end %>
diff --git a/app/views/templates_preferences/show.html.erb b/app/views/templates_preferences/show.html.erb
index 0fe4b5ee..b59fac43 100644
--- a/app/views/templates_preferences/show.html.erb
+++ b/app/views/templates_preferences/show.html.erb
@@ -41,25 +41,18 @@
<% end %>
- <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mb-2' }, data: { close_on_submit: false } do |f| %>
-
- <%= f.fields_for :preferences, Struct.new(:default_expire_at_duration, :default_expire_at).new(*@template.preferences.values_at('default_expire_at_duration', 'default_expire_at').compact_blank) do |ff| %>
+ <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mb-5' }, data: { close_on_submit: false } do |f| %>
+ <%= f.fields_for :preferences, Struct.new(:default_expire_at_duration, :default_expire_at).new(@template.preferences['default_expire_at_duration'], @template.preferences['default_expire_at'] ? Time.zone.parse(@template.preferences['default_expire_at']).in_time_zone(current_account.timezone) : nil) do |ff| %>
-
<% end %>
<% end %>
- <%= render 'templates_code_modal/preferences' %>
+ <%= render 'templates_code_modal/preferences', class: 'pt-2' %>
<% if show_recipients %>
diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml
index 7714cd35..d1d18c69 100644
--- a/config/locales/i18n.yml
+++ b/config/locales/i18n.yml
@@ -597,6 +597,7 @@ en: &en
not_invited_yet: Not invited yet
not_completed_yet: Not completed yet
declined_on_time: 'Declined on %{time}'
+ expire_on_time: 'Expire on %{time}'
sign_in_person: Sign In-person
create_a_new_template_document_form_or_submit_the_existing_one_html:
Create a new template document form or
submit the existing one
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Send email copy with completed documents to a specified BCC address.
@@ -1400,6 +1401,7 @@ es: &es
not_invited_yet: Aún no invitado
not_completed_yet: Aún no completado
declined_on_time: 'Rechazado el %{time}'
+ expire_on_time: 'Expira el %{time}'
sign_in_person: Firma en persona
create_a_new_template_document_form_or_submit_the_existing_one_html:
Crear una nueva plantilla de documento o
enviar el existente
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Enviar una copia del correo electrónico con los documentos completados a una dirección BCC especificada.
@@ -2202,6 +2204,7 @@ it: &it
not_invited_yet: Non ancora invitato
not_completed_yet: Non ancora completato
declined_on_time: 'Rifiutato il %{time}'
+ expire_on_time: 'Scade il %{time}'
sign_in_person: Firma di persona
create_a_new_template_document_form_or_submit_the_existing_one_html:
Crea un nuovo modello di documento o
invia quello esistente
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Invia una copia dell'email con i documenti completati a un indirizzo BCC specificato.
@@ -3006,6 +3009,7 @@ fr: &fr
not_invited_yet: Pas encore invité
not_completed_yet: Pas encore terminé
declined_on_time: 'Refusé le %{time}'
+ expire_on_time: 'Expire le %{time}'
sign_in_person: Signature en personne
create_a_new_template_document_form_or_submit_the_existing_one_html: "
Créer un nouveau modèle de document ou
soumettre l'existant"
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Envoyer une copie de l'e-mail avec les documents complétés à une adresse BCC spécifiée.
@@ -3809,6 +3813,7 @@ pt: &pt
not_invited_yet: Ainda não convidado
not_completed_yet: Ainda não concluído
declined_on_time: 'Recusado em %{time}'
+ expire_on_time: 'Expira em %{time}'
sign_in_person: Assinar pessoalmente
create_a_new_template_document_form_or_submit_the_existing_one_html: '
Criar um novo modelo de documento ou
submeter o existente'
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Envie uma cópia do e-mail com documentos concluídos para um endereço BCC especificado.
@@ -4613,6 +4618,7 @@ de: &de
not_invited_yet: Noch nicht eingeladen
not_completed_yet: Noch nicht abgeschlossen
declined_on_time: 'Abgelehnt am %{time}'
+ expire_on_time: 'Ablauf am %{time}'
sign_in_person: Persönlich unterschreiben
create_a_new_template_document_form_or_submit_the_existing_one_html: '
Neue Vorlage erstellen oder
vorhandene einreichen'
send_email_copy_with_completed_documents_to_a_specified_bcc_address: Senden Sie eine E-Mail-Kopie mit abgeschlossenen Dokumenten an eine angegebene BCC-Adresse.
diff --git a/lib/replace_email_variables.rb b/lib/replace_email_variables.rb
index ac7732ae..8ad9111b 100644
--- a/lib/replace_email_variables.rb
+++ b/lib/replace_email_variables.rb
@@ -15,6 +15,7 @@ module ReplaceEmailVariables
SUBMITTER_SLUG = /\{+submitter\.slug\}+/i
SUBMISSION_LINK = /\{+submission\.link\}+/i
SUBMISSION_ID = /\{+submission\.id\}+/i
+ SUBMISSION_EXPIRE_AT = /\{+submission\.expire_at\}+/i
SUBMITTERS = /\{+(?:submission\.)?submitters\}+/i
SUBMITTERS_N_EMAIL = /\{+submitters\[(?
\d+)\]\.email\}+/i
SUBMITTERS_N_NAME = /\{+submitters\[(?\d+)\]\.name\}+/i
@@ -48,6 +49,13 @@ module ReplaceEmailVariables
text = replace(text, SENDER_NAME, html_escape:) { submitter.submission.created_by_user&.full_name }
text = replace(text, SENDER_FIRST_NAME, html_escape:) { submitter.submission.created_by_user&.first_name }
+ text = replace(text, SUBMISSION_EXPIRE_AT, html_escape:) do
+ if submitter.submission.expire_at
+ I18n.l(submitter.submission.expire_at.in_time_zone(submitter.submission.account.timezone),
+ format: :short, locale: submitter.submission.account.locale)
+ end
+ end
+
text = replace(text, SUBMITTERS_N_NAME, html_escape:) do |match|
build_submitters_n_field(submitter.submission, match[:index].to_i - 1, :name)
end
diff --git a/lib/submissions.rb b/lib/submissions.rb
index ea59a83b..ada1d320 100644
--- a/lib/submissions.rb
+++ b/lib/submissions.rb
@@ -59,11 +59,13 @@ module Submissions
def create_from_emails(template:, user:, emails:, source:, mark_as_sent: false, params: {})
preferences = Submitters.normalize_preferences(user.account, user, params)
+ expire_at = params[:expire_at].presence || Templates.build_default_expire_at(template)
+
parse_emails(emails, user).uniq.map do |email|
submission = template.submissions.new(created_by_user: user,
account_id: user.account_id,
source:,
- expire_at: params[:expire_at].presence || build_default_expire_at(template),
+ expire_at:,
template_submitters: template.submitters)
submission.submitters.new(email: normalize_email(email),
@@ -208,17 +210,4 @@ module Submissions
Submissions::GenerateCombinedAttachment.call(submission.submitters.completed.order(:completed_at).last)
end
-
- def build_default_expire_at(template)
- default_expire_at_duration = template.preferences['default_expire_at_duration'].presence
- default_expire_at = template.preferences['default_expire_at'].presence
-
- return if default_expire_at_duration.blank?
-
- if default_expire_at_duration == 'specified_date' && default_expire_at.present?
- Time.zone.parse(default_expire_at)
- elsif Template::EXPIRATION_DURATIONS[default_expire_at_duration]
- Time.current.in_time_zone(template.account.timezone) + Template::EXPIRATION_DURATIONS[default_expire_at_duration]
- end
- end
end
diff --git a/lib/submissions/create_from_submitters.rb b/lib/submissions/create_from_submitters.rb
index bc9ee8d9..44adf95e 100644
--- a/lib/submissions/create_from_submitters.rb
+++ b/lib/submissions/create_from_submitters.rb
@@ -16,7 +16,7 @@ module Submissions
set_submission_preferences = submission_preferences.slice('send_email', 'bcc_completed')
set_submission_preferences['send_email'] = true if params['send_completed_email']
- expire_at = attrs[:expire_at] || Submissions.build_default_expire_at(template)
+ expire_at = attrs[:expire_at] || Templates.build_default_expire_at(template)
submission = template.submissions.new(created_by_user: user, source:,
account_id: user.account_id,
diff --git a/lib/templates.rb b/lib/templates.rb
index 48407d51..35651634 100644
--- a/lib/templates.rb
+++ b/lib/templates.rb
@@ -1,6 +1,25 @@
# frozen_string_literal: true
module Templates
+ EXPIRATION_DURATIONS = {
+ one_day: 1.day,
+ two_days: 2.days,
+ three_days: 3.days,
+ four_days: 4.days,
+ five_days: 5.days,
+ six_days: 6.days,
+ seven_days: 7.days,
+ eight_days: 8.days,
+ nine_days: 9.days,
+ ten_days: 10.days,
+ two_weeks: 14.days,
+ three_weeks: 21.days,
+ four_weeks: 28.days,
+ one_month: 1.month,
+ two_months: 2.months,
+ three_months: 3.months
+ }.with_indifferent_access.freeze
+
module_function
def build_field_areas_index(fields)
@@ -30,4 +49,17 @@ module Templates
item['linked_to_uuid'].blank? && item['is_requester'].blank? && item['email'].blank?
end
end
+
+ def build_default_expire_at(template)
+ default_expire_at_duration = template.preferences['default_expire_at_duration'].presence
+ default_expire_at = template.preferences['default_expire_at'].presence
+
+ return if default_expire_at_duration.blank?
+
+ if default_expire_at_duration == 'specified_date' && default_expire_at.present?
+ Time.zone.parse(default_expire_at)
+ elsif EXPIRATION_DURATIONS[default_expire_at_duration]
+ Time.current + EXPIRATION_DURATIONS[default_expire_at_duration]
+ end
+ end
end