rails_onboarding 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1351 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/rails_onboarding/admin/chart_controller.js +25 -0
- data/app/assets/javascripts/rails_onboarding/admin/filter_controller.js +49 -0
- data/app/assets/javascripts/rails_onboarding/admin/flash_controller.js +21 -0
- data/app/assets/javascripts/rails_onboarding/admin/flow_editor_controller.js +86 -0
- data/app/assets/javascripts/rails_onboarding/application.js +231 -0
- data/app/assets/javascripts/rails_onboarding/milestone_celebration_controller.js +177 -0
- data/app/assets/javascripts/rails_onboarding/milestone_dashboard_controller.js +132 -0
- data/app/assets/javascripts/rails_onboarding/milestone_detail_controller.js +34 -0
- data/app/assets/javascripts/rails_onboarding/navigation_controller.js +277 -0
- data/app/assets/javascripts/rails_onboarding/onboarding_controller.js +326 -0
- data/app/assets/javascripts/rails_onboarding/progress_controller.js +203 -0
- data/app/assets/javascripts/rails_onboarding/progressive_disclosure_controller.js +218 -0
- data/app/assets/javascripts/rails_onboarding/tooltip_controller.js +1237 -0
- data/app/assets/javascripts/rails_onboarding/tooltip_scheduler_controller.js +511 -0
- data/app/assets/javascripts/rails_onboarding/tour_controller.js +763 -0
- data/app/assets/stylesheets/rails_onboarding/accessibility.css +536 -0
- data/app/assets/stylesheets/rails_onboarding/admin.css +816 -0
- data/app/assets/stylesheets/rails_onboarding/application.css +1145 -0
- data/app/assets/stylesheets/rails_onboarding/flash_messages.css +237 -0
- data/app/assets/stylesheets/rails_onboarding/milestones.css +545 -0
- data/app/assets/stylesheets/rails_onboarding/mobile.css +856 -0
- data/app/assets/stylesheets/rails_onboarding/progressive_disclosure.css +299 -0
- data/app/assets/stylesheets/rails_onboarding/tooltips.css +468 -0
- data/app/assets/stylesheets/rails_onboarding/tour.css +495 -0
- data/app/assets/stylesheets/rails_onboarding/utilities.css +393 -0
- data/app/controllers/concerns/rails_onboarding/admin_authorization.rb +98 -0
- data/app/controllers/concerns/rails_onboarding/rate_limitable.rb +104 -0
- data/app/controllers/rails_onboarding/ab_tests_controller.rb +257 -0
- data/app/controllers/rails_onboarding/admin/ab_tests_controller.rb +121 -0
- data/app/controllers/rails_onboarding/admin/base_controller.rb +68 -0
- data/app/controllers/rails_onboarding/admin/dashboard_controller.rb +189 -0
- data/app/controllers/rails_onboarding/admin/flows_controller.rb +175 -0
- data/app/controllers/rails_onboarding/admin/users_controller.rb +366 -0
- data/app/controllers/rails_onboarding/application_controller.rb +9 -0
- data/app/controllers/rails_onboarding/milestones_controller.rb +126 -0
- data/app/controllers/rails_onboarding/onboarding_controller.rb +409 -0
- data/app/controllers/rails_onboarding/progressive_features_controller.rb +134 -0
- data/app/controllers/rails_onboarding/templates_controller.rb +162 -0
- data/app/controllers/rails_onboarding/test_sessions_controller.rb +16 -0
- data/app/controllers/rails_onboarding/tooltips_controller.rb +92 -0
- data/app/helpers/rails_onboarding/application_helper.rb +4 -0
- data/app/helpers/rails_onboarding/i18n_helper.rb +137 -0
- data/app/helpers/rails_onboarding/personalization_helper.rb +184 -0
- data/app/helpers/rails_onboarding/templates_helper.rb +253 -0
- data/app/jobs/rails_onboarding/application_job.rb +4 -0
- data/app/mailers/rails_onboarding/application_mailer.rb +6 -0
- data/app/mailers/rails_onboarding/onboarding_mailer.rb +72 -0
- data/app/models/concerns/rails_onboarding/ab_testable.rb +149 -0
- data/app/models/concerns/rails_onboarding/onboardable.rb +649 -0
- data/app/models/concerns/rails_onboarding/personalizable.rb +211 -0
- data/app/models/concerns/rails_onboarding/progressive_disclosure.rb +276 -0
- data/app/models/rails_onboarding/analytics.rb +255 -0
- data/app/models/rails_onboarding/analytics_event.rb +168 -0
- data/app/models/rails_onboarding/application_record.rb +5 -0
- data/app/models/rails_onboarding/flow.rb +41 -0
- data/app/services/rails_onboarding/milestone_service.rb +105 -0
- data/app/views/layouts/rails_onboarding/admin.html.erb +106 -0
- data/app/views/layouts/rails_onboarding/application.html.erb +65 -0
- data/app/views/rails_onboarding/ab_tests/index.html.erb +209 -0
- data/app/views/rails_onboarding/ab_tests/results.html.erb +253 -0
- data/app/views/rails_onboarding/admin/ab_tests/_ab_test_card.html.erb +24 -0
- data/app/views/rails_onboarding/admin/ab_tests/index.html.erb +57 -0
- data/app/views/rails_onboarding/admin/ab_tests/show.html.erb +41 -0
- data/app/views/rails_onboarding/admin/dashboard/index.html.erb +224 -0
- data/app/views/rails_onboarding/admin/flows/_form.html.erb +147 -0
- data/app/views/rails_onboarding/admin/flows/edit.html.erb +7 -0
- data/app/views/rails_onboarding/admin/flows/index.html.erb +125 -0
- data/app/views/rails_onboarding/admin/flows/new.html.erb +7 -0
- data/app/views/rails_onboarding/admin/flows/preview.html.erb +40 -0
- data/app/views/rails_onboarding/admin/flows/show.html.erb +114 -0
- data/app/views/rails_onboarding/admin/users/index.html.erb +180 -0
- data/app/views/rails_onboarding/admin/users/show.html.erb +187 -0
- data/app/views/rails_onboarding/milestones/index.html.erb +64 -0
- data/app/views/rails_onboarding/milestones/show.html.erb +57 -0
- data/app/views/rails_onboarding/onboarding/_custom_footer.html.erb +2 -0
- data/app/views/rails_onboarding/onboarding/_progress_indicator.html.erb +56 -0
- data/app/views/rails_onboarding/onboarding/_step_navigation.html.erb +44 -0
- data/app/views/rails_onboarding/onboarding/step.html.erb +38 -0
- data/app/views/rails_onboarding/onboarding/welcome.html.erb +37 -0
- data/app/views/rails_onboarding/onboarding_mailer/completion_email.html.erb +109 -0
- data/app/views/rails_onboarding/onboarding_mailer/completion_email.text.erb +29 -0
- data/app/views/rails_onboarding/onboarding_mailer/reminder_email.html.erb +99 -0
- data/app/views/rails_onboarding/onboarding_mailer/reminder_email.text.erb +20 -0
- data/app/views/rails_onboarding/onboarding_mailer/step_completed_email.html.erb +105 -0
- data/app/views/rails_onboarding/onboarding_mailer/step_completed_email.text.erb +19 -0
- data/app/views/rails_onboarding/onboarding_mailer/welcome_email.html.erb +74 -0
- data/app/views/rails_onboarding/onboarding_mailer/welcome_email.text.erb +17 -0
- data/app/views/rails_onboarding/shared/_flash.html.erb +25 -0
- data/app/views/rails_onboarding/shared/_milestone_badge.html.erb +22 -0
- data/app/views/rails_onboarding/shared/_milestone_celebration.html.erb +44 -0
- data/app/views/rails_onboarding/shared/_onboarding_banner.html.erb +48 -0
- data/app/views/rails_onboarding/templates/index.html.erb +255 -0
- data/config/importmap.rb +33 -0
- data/config/locales/en.yml +62 -0
- data/config/locales/es.yml +62 -0
- data/config/locales/fr.yml +62 -0
- data/config/routes.rb +79 -0
- data/lib/generators/rails_onboarding/install_generator.rb +124 -0
- data/lib/generators/rails_onboarding/templates/README +120 -0
- data/lib/generators/rails_onboarding/templates/add_analytics_to_rails_onboarding.rb +33 -0
- data/lib/generators/rails_onboarding/templates/add_milestone_tracking_to_users.rb +38 -0
- data/lib/generators/rails_onboarding/templates/add_onboarding_indexes.rb +93 -0
- data/lib/generators/rails_onboarding/templates/add_onboarding_to_users.rb +51 -0
- data/lib/generators/rails_onboarding/templates/add_robustness_fields_to_users.rb.tt +20 -0
- data/lib/generators/rails_onboarding/templates/create_rails_onboarding_flows.rb +18 -0
- data/lib/generators/rails_onboarding/templates/onboarding.css +18 -0
- data/lib/generators/rails_onboarding/templates/rails_onboarding.rb +75 -0
- data/lib/generators/rails_onboarding/update_generator.rb +73 -0
- data/lib/rails_onboarding/api_mode.rb +367 -0
- data/lib/rails_onboarding/backfill.rb +130 -0
- data/lib/rails_onboarding/background_jobs.rb +412 -0
- data/lib/rails_onboarding/caching.rb +180 -0
- data/lib/rails_onboarding/cdn_support.rb +202 -0
- data/lib/rails_onboarding/configuration/ab_testing.rb +24 -0
- data/lib/rails_onboarding/configuration/analytics.rb +29 -0
- data/lib/rails_onboarding/configuration/integrations.rb +31 -0
- data/lib/rails_onboarding/configuration/milestones.rb +92 -0
- data/lib/rails_onboarding/configuration/personalization.rb +25 -0
- data/lib/rails_onboarding/configuration/progressive_disclosure.rb +41 -0
- data/lib/rails_onboarding/configuration/rate_limiting.rb +18 -0
- data/lib/rails_onboarding/configuration/steps.rb +121 -0
- data/lib/rails_onboarding/configuration/templates.rb +85 -0
- data/lib/rails_onboarding/configuration/tooltips.rb +32 -0
- data/lib/rails_onboarding/configuration.rb +133 -0
- data/lib/rails_onboarding/configuration_errors.rb +19 -0
- data/lib/rails_onboarding/configuration_validator.rb +477 -0
- data/lib/rails_onboarding/controller_helpers.rb +153 -0
- data/lib/rails_onboarding/current.rb +13 -0
- data/lib/rails_onboarding/deprecation.rb +64 -0
- data/lib/rails_onboarding/devise_integration.rb +122 -0
- data/lib/rails_onboarding/engine.rb +245 -0
- data/lib/rails_onboarding/error_recovery.rb +173 -0
- data/lib/rails_onboarding/lazy_loading.rb +155 -0
- data/lib/rails_onboarding/multi_tenant.rb +306 -0
- data/lib/rails_onboarding/requirements_validator.rb +315 -0
- data/lib/rails_onboarding/responsive_helper.rb +218 -0
- data/lib/rails_onboarding/session_manager.rb +187 -0
- data/lib/rails_onboarding/skip_logic.rb +159 -0
- data/lib/rails_onboarding/turbo_compatibility.rb +229 -0
- data/lib/rails_onboarding/version.rb +3 -0
- data/lib/rails_onboarding.rb +44 -0
- data/lib/tasks/rails_onboarding_analytics.rake +136 -0
- data/lib/tasks/rails_onboarding_tasks.rake +162 -0
- metadata +303 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module RailsOnboarding
|
|
2
|
+
class OnboardingMailer < ApplicationMailer
|
|
3
|
+
include RailsOnboarding::Engine.routes.url_helpers
|
|
4
|
+
|
|
5
|
+
default from: -> { RailsOnboarding.configuration.mailer_from || "noreply@example.com" }
|
|
6
|
+
|
|
7
|
+
def default_url_options
|
|
8
|
+
if defined?(Rails) && Rails.application
|
|
9
|
+
Rails.application.config.action_mailer.default_url_options || { host: "localhost", port: 3000 }
|
|
10
|
+
else
|
|
11
|
+
{ host: "localhost", port: 3000 }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def welcome_email(user)
|
|
16
|
+
@user = user
|
|
17
|
+
@onboarding_url = onboarding_url
|
|
18
|
+
|
|
19
|
+
mail(
|
|
20
|
+
to: user.email,
|
|
21
|
+
subject: I18n.t("rails_onboarding.mailer.welcome.subject", default: "Welcome! Get Started")
|
|
22
|
+
) do |format|
|
|
23
|
+
format.text
|
|
24
|
+
format.html
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reminder_email(user)
|
|
29
|
+
@user = user
|
|
30
|
+
@current_step = user.onboarding_current_step
|
|
31
|
+
@progress = user.onboarding_progress_percentage
|
|
32
|
+
@onboarding_url = onboarding_url
|
|
33
|
+
|
|
34
|
+
mail(
|
|
35
|
+
to: user.email,
|
|
36
|
+
subject: I18n.t("rails_onboarding.mailer.reminder.subject", default: "Complete Your Onboarding")
|
|
37
|
+
) do |format|
|
|
38
|
+
format.text
|
|
39
|
+
format.html
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def completion_email(user)
|
|
44
|
+
@user = user
|
|
45
|
+
@completed_at = user.onboarding_completed_at
|
|
46
|
+
@milestone_points = user.respond_to?(:milestone_points) ? user.milestone_points : 0
|
|
47
|
+
|
|
48
|
+
mail(
|
|
49
|
+
to: user.email,
|
|
50
|
+
subject: I18n.t("rails_onboarding.mailer.completion.subject", default: "Congratulations! Onboarding Complete")
|
|
51
|
+
) do |format|
|
|
52
|
+
format.text
|
|
53
|
+
format.html
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def step_completed_email(user)
|
|
58
|
+
@user = user
|
|
59
|
+
@step = user.onboarding_current_step
|
|
60
|
+
@progress = user.onboarding_progress_percentage
|
|
61
|
+
@onboarding_url = onboarding_url
|
|
62
|
+
|
|
63
|
+
mail(
|
|
64
|
+
to: user.email,
|
|
65
|
+
subject: I18n.t("rails_onboarding.mailer.step_completed.subject", default: "Step Completed!")
|
|
66
|
+
) do |format|
|
|
67
|
+
format.text
|
|
68
|
+
format.html
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsOnboarding
|
|
4
|
+
# Concern for A/B testing capabilities on User models
|
|
5
|
+
# Provides methods to assign users to test variants and track their performance
|
|
6
|
+
#
|
|
7
|
+
# @example Include in your User model
|
|
8
|
+
# class User < ApplicationRecord
|
|
9
|
+
# include RailsOnboarding::Onboardable
|
|
10
|
+
# include RailsOnboarding::AbTestable
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# @example Configure A/B tests
|
|
14
|
+
# RailsOnboarding.configure do |config|
|
|
15
|
+
# config.ab_tests = {
|
|
16
|
+
# onboarding_flow: {
|
|
17
|
+
# variants: ['original', 'simplified', 'gamified'],
|
|
18
|
+
# weights: [50, 25, 25], # Percentage distribution
|
|
19
|
+
# enabled: true
|
|
20
|
+
# }
|
|
21
|
+
# }
|
|
22
|
+
# end
|
|
23
|
+
module AbTestable
|
|
24
|
+
extend ActiveSupport::Concern
|
|
25
|
+
|
|
26
|
+
included do
|
|
27
|
+
# Store A/B test assignments as JSON
|
|
28
|
+
# Expected column: ab_test_assignments (jsonb or text)
|
|
29
|
+
serialize :ab_test_assignments, coder: JSON unless column_names.include?("ab_test_assignments") && columns_hash["ab_test_assignments"].type == :jsonb
|
|
30
|
+
|
|
31
|
+
# Callbacks
|
|
32
|
+
after_initialize :assign_ab_test_variants, if: :new_record?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Get the variant for a specific A/B test
|
|
36
|
+
#
|
|
37
|
+
# @param test_name [String, Symbol] Name of the A/B test
|
|
38
|
+
# @return [String, nil] The assigned variant name or nil if not assigned
|
|
39
|
+
#
|
|
40
|
+
# @example
|
|
41
|
+
# user.ab_test_variant(:onboarding_flow)
|
|
42
|
+
# # => "simplified"
|
|
43
|
+
def ab_test_variant(test_name)
|
|
44
|
+
return nil unless ab_test_assignments.is_a?(Hash)
|
|
45
|
+
|
|
46
|
+
ab_test_assignments[test_name.to_s]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Check if user is in a specific variant
|
|
50
|
+
#
|
|
51
|
+
# @param test_name [String, Symbol] Name of the A/B test
|
|
52
|
+
# @param variant_name [String, Symbol] Name of the variant to check
|
|
53
|
+
# @return [Boolean] True if user is in the specified variant
|
|
54
|
+
#
|
|
55
|
+
# @example
|
|
56
|
+
# user.in_variant?(:onboarding_flow, :simplified)
|
|
57
|
+
# # => true
|
|
58
|
+
def in_variant?(test_name, variant_name)
|
|
59
|
+
ab_test_variant(test_name) == variant_name.to_s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Assign a specific variant to the user (useful for manual assignment)
|
|
63
|
+
#
|
|
64
|
+
# @param test_name [String, Symbol] Name of the A/B test
|
|
65
|
+
# @param variant_name [String, Symbol] Name of the variant to assign
|
|
66
|
+
# @return [Boolean] True if assignment was successful
|
|
67
|
+
#
|
|
68
|
+
# @example
|
|
69
|
+
# user.assign_variant(:onboarding_flow, :gamified)
|
|
70
|
+
def assign_variant(test_name, variant_name)
|
|
71
|
+
self.ab_test_assignments ||= {}
|
|
72
|
+
self.ab_test_assignments[test_name.to_s] = variant_name.to_s
|
|
73
|
+
save if persisted?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get all A/B test assignments for this user
|
|
77
|
+
#
|
|
78
|
+
# @return [Hash] Hash of test_name => variant_name
|
|
79
|
+
def all_ab_test_assignments
|
|
80
|
+
ab_test_assignments || {}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Track a conversion for an A/B test
|
|
84
|
+
#
|
|
85
|
+
# @param test_name [String, Symbol] Name of the A/B test
|
|
86
|
+
# @param event_name [String, Symbol] Name of the conversion event
|
|
87
|
+
# @param metadata [Hash] Additional metadata to track
|
|
88
|
+
#
|
|
89
|
+
# @example
|
|
90
|
+
# user.track_ab_conversion(:onboarding_flow, :completed, { time_spent: 300 })
|
|
91
|
+
def track_ab_conversion(test_name, event_name, metadata = {})
|
|
92
|
+
variant = ab_test_variant(test_name)
|
|
93
|
+
return unless variant
|
|
94
|
+
|
|
95
|
+
# Track using analytics system if available
|
|
96
|
+
if respond_to?(:track_analytics_event)
|
|
97
|
+
track_analytics_event(
|
|
98
|
+
"ab_test_conversion",
|
|
99
|
+
{
|
|
100
|
+
test_name: test_name.to_s,
|
|
101
|
+
variant: variant,
|
|
102
|
+
event_name: event_name.to_s
|
|
103
|
+
}.merge(metadata)
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
# Automatically assign variants for all enabled A/B tests on user creation
|
|
111
|
+
def assign_ab_test_variants
|
|
112
|
+
self.ab_test_assignments ||= {}
|
|
113
|
+
|
|
114
|
+
ab_tests = RailsOnboarding.configuration.ab_tests || {}
|
|
115
|
+
|
|
116
|
+
ab_tests.each do |test_name, config|
|
|
117
|
+
next unless config[:enabled]
|
|
118
|
+
next if ab_test_assignments[test_name.to_s].present?
|
|
119
|
+
|
|
120
|
+
variant = select_variant(config[:variants], config[:weights])
|
|
121
|
+
ab_test_assignments[test_name.to_s] = variant
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Select a variant based on weights
|
|
126
|
+
#
|
|
127
|
+
# @param variants [Array<String>] Available variants
|
|
128
|
+
# @param weights [Array<Integer>] Percentage weights for each variant
|
|
129
|
+
# @return [String] Selected variant name
|
|
130
|
+
def select_variant(variants, weights = nil)
|
|
131
|
+
return variants.sample if weights.nil? || weights.empty?
|
|
132
|
+
|
|
133
|
+
# Normalize weights to ensure they sum to 100
|
|
134
|
+
total_weight = weights.sum.to_f
|
|
135
|
+
normalized_weights = weights.map { |w| (w / total_weight * 100).to_i }
|
|
136
|
+
|
|
137
|
+
# Generate random number and select variant
|
|
138
|
+
random = rand(100)
|
|
139
|
+
cumulative = 0
|
|
140
|
+
|
|
141
|
+
variants.each_with_index do |variant, index|
|
|
142
|
+
cumulative += normalized_weights[index]
|
|
143
|
+
return variant if random < cumulative
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
variants.last # Fallback to last variant
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|