mirror of https://github.com/docusealco/docuseal
- Add ProcessSubmitterRemindersJob (hourly recurrent via Sidekiq) - Add SendSubmitterReminderEmailJob for individual reminder dispatch - Add scheduled_jobs initializer to bootstrap the reminder job - Add reminder configuration UI in notification settings - Skip reminders for completed, declined, or archived submissionspull/681/head
parent
df3697a64f
commit
82e82635e7
@ -0,0 +1,94 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ProcessSubmitterRemindersJob
|
||||||
|
include Sidekiq::Job
|
||||||
|
|
||||||
|
sidekiq_options queue: :recurrent
|
||||||
|
|
||||||
|
def perform
|
||||||
|
AccountConfig.where(key: AccountConfig::SUBMITTER_REMINDERS).find_each do |config|
|
||||||
|
process_account_reminders(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
ProcessSubmitterRemindersJob.perform_in(1.hour)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_account_reminders(config)
|
||||||
|
durations = parse_durations(config.value)
|
||||||
|
return if durations.empty?
|
||||||
|
|
||||||
|
pending_submitters = Submitter
|
||||||
|
.joins(:submission)
|
||||||
|
.where(account_id: config.account_id)
|
||||||
|
.where.not(sent_at: nil)
|
||||||
|
.where(completed_at: nil, declined_at: nil)
|
||||||
|
.where.not(email: [nil, ''])
|
||||||
|
.where(submissions: { archived_at: nil })
|
||||||
|
|
||||||
|
pending_submitters.find_each do |submitter|
|
||||||
|
next if submitter.template&.archived_at?
|
||||||
|
|
||||||
|
send_reminder_if_due(submitter, durations)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_reminder_if_due(submitter, durations)
|
||||||
|
reminder_count = submitter.submission_events.where(event_type: 'send_reminder_email').count
|
||||||
|
|
||||||
|
duration = case reminder_count
|
||||||
|
when 0 then durations[:first]
|
||||||
|
when 1 then durations[:second]
|
||||||
|
when 2 then durations[:third]
|
||||||
|
else return
|
||||||
|
end
|
||||||
|
|
||||||
|
return unless duration
|
||||||
|
|
||||||
|
base_time = if reminder_count == 0
|
||||||
|
submitter.sent_at
|
||||||
|
else
|
||||||
|
submitter.submission_events
|
||||||
|
.where(event_type: 'send_reminder_email')
|
||||||
|
.order(:created_at)
|
||||||
|
.last&.created_at || submitter.sent_at
|
||||||
|
end
|
||||||
|
|
||||||
|
return if base_time.nil?
|
||||||
|
return unless Time.current >= base_time + duration
|
||||||
|
|
||||||
|
SendSubmitterReminderEmailJob.perform_async('submitter_id' => submitter.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_durations(value)
|
||||||
|
return {} unless value.is_a?(Hash)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result[:first] = duration_to_seconds(value['first_duration']) if value['first_duration'].present?
|
||||||
|
result[:second] = duration_to_seconds(value['second_duration']) if value['second_duration'].present?
|
||||||
|
result[:third] = duration_to_seconds(value['third_duration']) if value['third_duration'].present?
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def duration_to_seconds(key)
|
||||||
|
case key
|
||||||
|
when 'one_hour' then 1.hour
|
||||||
|
when 'two_hours' then 2.hours
|
||||||
|
when 'four_hours' then 4.hours
|
||||||
|
when 'eight_hours' then 8.hours
|
||||||
|
when 'twelve_hours' then 12.hours
|
||||||
|
when 'twenty_four_hours' then 24.hours
|
||||||
|
when 'two_days' then 2.days
|
||||||
|
when 'three_days' then 3.days
|
||||||
|
when 'four_days' then 4.days
|
||||||
|
when 'five_days' then 5.days
|
||||||
|
when 'six_days' then 6.days
|
||||||
|
when 'seven_days' then 7.days
|
||||||
|
when 'eight_days' then 8.days
|
||||||
|
when 'fifteen_days' then 15.days
|
||||||
|
when 'twenty_one_days' then 21.days
|
||||||
|
when 'thirty_days' then 30.days
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SendSubmitterReminderEmailJob
|
||||||
|
include Sidekiq::Job
|
||||||
|
|
||||||
|
sidekiq_options queue: :mailers
|
||||||
|
|
||||||
|
def perform(params = {})
|
||||||
|
submitter = Submitter.find(params['submitter_id'])
|
||||||
|
|
||||||
|
return if submitter.completed_at?
|
||||||
|
return if submitter.declined_at?
|
||||||
|
return if submitter.submission.archived_at?
|
||||||
|
return if submitter.template&.archived_at?
|
||||||
|
return unless submitter.email.to_s.include?('@')
|
||||||
|
return unless Accounts.can_send_emails?(submitter.account)
|
||||||
|
|
||||||
|
mail = SubmitterMailer.invitation_email(submitter)
|
||||||
|
|
||||||
|
mail.deliver_now!
|
||||||
|
|
||||||
|
SubmissionEvent.create!(submitter:, event_type: 'send_reminder_email')
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1 +0,0 @@
|
|||||||
<%= render 'reminder_placeholder' %>
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<div class="alert my-4">
|
|
||||||
<%= svg_icon('info_circle', class: 'w-6 h-6') %>
|
|
||||||
<div>
|
|
||||||
<p class="font-bold">
|
|
||||||
<%= t('unlock_with_docuseal_pro') %>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<%= t('send_automatic_email_reminders_to_your_recipients') %>
|
|
||||||
<br>
|
|
||||||
<a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}" %>" data-turbo="false">
|
|
||||||
<%= t('learn_more') %>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:sidekiq_config) do
|
||||||
|
ProcessSubmitterRemindersJob.perform_in(1.minute)
|
||||||
|
end
|
||||||
Loading…
Reference in new issue