add submission tracking events
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class SubmitterEmailClicksController < ApiBaseController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||
|
||||
if params[:t] == SubmissionEvents.build_tracking_param(submitter, 'click_email')
|
||||
SubmissionEvents.create_with_tracking_data(submitter, 'click_email', request)
|
||||
end
|
||||
|
||||
render json: {}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class SubmitterFormViewsController < ApiBaseController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||
|
||||
SubmissionEvents.create_with_tracking_data(submitter, 'view_form', request)
|
||||
|
||||
render json: {}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,8 @@ class SubmittersSendEmailController < ApplicationController
|
||||
|
||||
SubmitterMailer.invitation_email(submitter).deliver_later!
|
||||
|
||||
SubmissionEvent.create!(submitter:, event_type: 'send_email')
|
||||
|
||||
submitter.sent_at ||= Time.current
|
||||
submitter.save!
|
||||
|
||||
|
||||
@@ -493,10 +493,44 @@ export default {
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.recalculateButtonDisabledKey = Math.random()
|
||||
|
||||
this.maybeTrackEmailClick()
|
||||
this.trackViewForm()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
t,
|
||||
maybeTrackEmailClick () {
|
||||
const queryParams = new URLSearchParams(window.location.search)
|
||||
|
||||
if (queryParams.has('t')) {
|
||||
fetch(this.baseUrl + '/api/submitter_email_clicks', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
t: queryParams.get('t'),
|
||||
submitter_slug: this.submitterSlug
|
||||
})
|
||||
})
|
||||
|
||||
queryParams.delete('t')
|
||||
const newUrl = [window.location.pathname, queryParams.toString()].filter(Boolean).join('?')
|
||||
window.history.replaceState({}, document.title, newUrl)
|
||||
}
|
||||
},
|
||||
trackViewForm () {
|
||||
fetch(this.baseUrl + '/api/submitter_form_views', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
submitter_slug: this.submitterSlug
|
||||
})
|
||||
})
|
||||
},
|
||||
goToStep (step, scrollToArea = false, clickUpload = false) {
|
||||
this.currentStep = this.stepFields.indexOf(step)
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ class SendSubmitterInvitationEmailJob < ApplicationJob
|
||||
def perform(submitter)
|
||||
SubmitterMailer.invitation_email(submitter).deliver_now!
|
||||
|
||||
SubmissionEvent.create!(submitter:, event_type: 'send_email')
|
||||
|
||||
submitter.sent_at ||= Time.current
|
||||
submitter.save
|
||||
end
|
||||
|
||||
@@ -32,6 +32,7 @@ class Submission < ApplicationRecord
|
||||
belongs_to :created_by_user, class_name: 'User', optional: true
|
||||
|
||||
has_many :submitters, dependent: :destroy
|
||||
has_many :submission_events, dependent: :destroy
|
||||
|
||||
serialize :template_fields, JSON
|
||||
serialize :template_schema, JSON
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: submission_events
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# data :text not null
|
||||
# event_timestamp :datetime not null
|
||||
# event_type :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# submission_id :bigint not null
|
||||
# submitter_id :bigint
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_submission_events_on_submission_id (submission_id)
|
||||
# index_submission_events_on_submitter_id (submitter_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (submission_id => submissions.id)
|
||||
# fk_rails_... (submitter_id => submitters.id)
|
||||
#
|
||||
class SubmissionEvent < ApplicationRecord
|
||||
belongs_to :submission
|
||||
belongs_to :submitter, optional: true
|
||||
|
||||
attribute :data, :string, default: -> { {} }
|
||||
attribute :event_timestamp, :datetime, default: -> { Time.current }
|
||||
|
||||
serialize :data, JSON
|
||||
|
||||
before_validation :set_submission_id, on: :create
|
||||
|
||||
enum :event_type, {
|
||||
send_email: 'send_email',
|
||||
send_sms: 'send_sms',
|
||||
open_email: 'open_email',
|
||||
click_email: 'click_email',
|
||||
click_sms: 'click_sms',
|
||||
start_form: 'start_form',
|
||||
view_form: 'view_form',
|
||||
complete_form: 'complete_form'
|
||||
}, scope: false
|
||||
|
||||
private
|
||||
|
||||
def set_submission_id
|
||||
self.submission_id = submitter&.submission_id
|
||||
end
|
||||
end
|
||||
@@ -44,6 +44,7 @@ class Submitter < ApplicationRecord
|
||||
has_many_attached :attachments
|
||||
|
||||
has_many :document_generation_events, dependent: :destroy
|
||||
has_many :submission_events, dependent: :destroy
|
||||
|
||||
def status
|
||||
if completed_at?
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<% else %>
|
||||
<p>Hi there,</p>
|
||||
<%= simple_format(@message) %>
|
||||
<p><%= link_to 'Submit Form', submit_form_url(slug: @submitter.slug) %></p>
|
||||
<p><%= link_to 'Submit Form', submit_form_url(slug: @submitter.slug, t: SubmissionEvents.build_tracking_param(@submitter, 'click_email')) %></p>
|
||||
<p>Please contact us by replying to this email if you didn't request this.</p>
|
||||
<p>
|
||||
Thanks,<br><%= @current_account.name %>
|
||||
|
||||
@@ -31,6 +31,8 @@ Rails.application.routes.draw do
|
||||
|
||||
namespace :api, defaults: { format: :json } do
|
||||
resources :attachments, only: %i[create]
|
||||
resources :submitter_email_clicks, only: %i[create]
|
||||
resources :submitter_form_views, only: %i[create]
|
||||
resources :submissions, only: %i[create]
|
||||
resources :templates, only: %i[update show index] do
|
||||
resources :submissions, only: %i[create]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateSubmissionEvents < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :submission_events do |t|
|
||||
t.references :submission, null: false, foreign_key: true, index: true
|
||||
t.references :submitter, null: true, foreign_key: true, index: true
|
||||
t.text :data, null: false
|
||||
t.string :event_type, null: false
|
||||
t.datetime :event_timestamp, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
+15
-1
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_09_09_213212) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_09_10_084410) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
@@ -91,6 +91,18 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_09_213212) do
|
||||
t.index ["account_id"], name: "index_encrypted_configs_on_account_id"
|
||||
end
|
||||
|
||||
create_table "submission_events", force: :cascade do |t|
|
||||
t.bigint "submission_id", null: false
|
||||
t.bigint "submitter_id"
|
||||
t.text "data", null: false
|
||||
t.string "event_type", null: false
|
||||
t.datetime "event_timestamp", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["submission_id"], name: "index_submission_events_on_submission_id"
|
||||
t.index ["submitter_id"], name: "index_submission_events_on_submitter_id"
|
||||
end
|
||||
|
||||
create_table "submissions", force: :cascade do |t|
|
||||
t.bigint "template_id", null: false
|
||||
t.bigint "created_by_user_id"
|
||||
@@ -178,6 +190,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_09_213212) do
|
||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "document_generation_events", "submitters"
|
||||
add_foreign_key "encrypted_configs", "accounts"
|
||||
add_foreign_key "submission_events", "submissions"
|
||||
add_foreign_key "submission_events", "submitters"
|
||||
add_foreign_key "submissions", "templates"
|
||||
add_foreign_key "submissions", "users", column: "created_by_user_id"
|
||||
add_foreign_key "submitters", "submissions"
|
||||
|
||||
@@ -45,7 +45,9 @@ module ReplaceEmailVariables
|
||||
|
||||
def build_submitter_link(submitter)
|
||||
Rails.application.routes.url_helpers.submit_form_url(
|
||||
slug: submitter.slug, **Docuseal.default_url_options
|
||||
slug: submitter.slug,
|
||||
t: SubmissionEvents.build_tracking_param(submitter, 'click_email'),
|
||||
**Docuseal.default_url_options
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SubmissionEvents
|
||||
TRACKING_PARAM_LENGTH = 6
|
||||
|
||||
module_function
|
||||
|
||||
def build_tracking_param(submitter, event_type = 'click_email')
|
||||
Base64.urlsafe_encode64(
|
||||
[submitter.slug, event_type, Rails.application.secrets.secret_key_base].join(':')
|
||||
).first(TRACKING_PARAM_LENGTH)
|
||||
end
|
||||
|
||||
def create_with_tracking_data(submitter, event_type, request)
|
||||
SubmissionEvent.create!(submitter:, event_type:, data: {
|
||||
ip: request.remote_ip,
|
||||
ua: request.user_agent,
|
||||
sid: request.session.id.to_s,
|
||||
uid: request.env['warden'].user(:user)&.id
|
||||
}.compact_blank)
|
||||
end
|
||||
end
|
||||
@@ -7,7 +7,11 @@ module Submitters
|
||||
module_function
|
||||
|
||||
def call(submitter, params, request)
|
||||
Submissions.update_template_fields!(submitter.submission) if submitter.submission.template_fields.blank?
|
||||
if submitter.submission.template_fields.blank?
|
||||
Submissions.update_template_fields!(submitter.submission)
|
||||
|
||||
SubmissionEvents.create_with_tracking_data(submitter, 'start_form', request)
|
||||
end
|
||||
|
||||
update_submitter!(submitter, params, request)
|
||||
|
||||
@@ -32,6 +36,8 @@ module Submitters
|
||||
submitter.completed_at = Time.current
|
||||
submitter.ip = request.remote_ip
|
||||
submitter.ua = request.user_agent
|
||||
|
||||
SubmissionEvents.create_with_tracking_data(submitter, 'complete_form', request)
|
||||
end
|
||||
|
||||
submitter.save!
|
||||
|
||||
Reference in New Issue
Block a user