From 19a9e9aa8b3d7f119281dec796de6c139655b71a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 14 Feb 2018 22:16:32 -0600 Subject: [PATCH 01/13] timezone feature --- admin/app/admin/jam_ruby_users.rb | 13 ++++ admin/app/views/admin/users/_form.html.slim | 1 + db/manifest | 3 +- db/up/user_timezone.sql | 2 + ruby/lib/jam_ruby/app/mailers/admin_mailer.rb | 9 +++ ruby/lib/jam_ruby/models/anonymous_user.rb | 4 ++ ruby/lib/jam_ruby/models/lesson_booking.rb | 3 +- .../jam_ruby/models/lesson_booking_slot.rb | 12 ++-- ruby/lib/jam_ruby/models/lesson_session.rb | 2 + ruby/lib/jam_ruby/models/music_session.rb | 5 +- ruby/lib/jam_ruby/models/user.rb | 25 +++++++- .../jam_ruby/models/lesson_session_spec.rb | 15 +---- web/app/assets/javascripts/basic/basic.js | 1 + .../javascripts/everywhere/everywhere.js | 9 +++ web/app/assets/javascripts/landing/landing.js | 1 + .../LessonBooking.js.jsx.coffee | 25 ++++++++ web/app/assets/javascripts/web/web.js | 1 + web/app/assets/stylesheets/client/toggle.scss | 34 +++++++++++ web/app/controllers/api_recurly_controller.rb | 3 +- web/app/controllers/api_users_controller.rb | 3 +- web/app/controllers/landings_controller.rb | 3 +- web/app/controllers/sessions_controller.rb | 4 +- web/app/controllers/users_controller.rb | 1 + web/app/helpers/music_session_helper.rb | 4 +- web/app/helpers/sessions_helper.rb | 4 ++ web/app/views/api_lesson_bookings/show.rabl | 38 ++++++++++-- web/app/views/api_lesson_sessions/show.rabl | 8 ++- web/app/views/api_users/show.rabl | 2 +- web/app/views/clients/_profile.html.erb | 5 +- web/lib/user_manager.rb | 4 +- web/spec/features/activate_account_spec.rb | 61 ++++++++++--------- .../active_music_sessions_api_spec.rb | 8 +-- 32 files changed, 242 insertions(+), 71 deletions(-) create mode 100644 db/up/user_timezone.sql diff --git a/admin/app/admin/jam_ruby_users.rb b/admin/app/admin/jam_ruby_users.rb index cce181faf..c079792e9 100644 --- a/admin/app/admin/jam_ruby_users.rb +++ b/admin/app/admin/jam_ruby_users.rb @@ -16,6 +16,13 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do form :partial => "form" + member_action :delete_forever, :method => :get do + resource.permanently_delete + redirect_to :back, {notice: 'User email and login credentials have been permanently changed'} + end + + + show do |user| panel "Common" do attributes_table do @@ -60,6 +67,11 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do end end end + row "Delete Forever" do |user| + span do + link_to("delete forever", delete_forever_admin_user_path(user.id), :data => {:confirm => 'Are you sure?'}) + end + end row :jamclass_credits row :via_amazon @@ -243,6 +255,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do @user.email = params[:jam_ruby_user][:email] @user.admin = params[:jam_ruby_user][:admin] @user.is_onboarder = params[:jam_ruby_user][:is_onboarder] + @user.subscribe_email = params[:jam_ruby_user][:subscribe_email] @user.musician = params[:jam_ruby_user][:musician] @user.first_name = params[:jam_ruby_user][:first_name] @user.last_name = params[:jam_ruby_user][:last_name] diff --git a/admin/app/views/admin/users/_form.html.slim b/admin/app/views/admin/users/_form.html.slim index 2e8d483b9..bb666c081 100644 --- a/admin/app/views/admin/users/_form.html.slim +++ b/admin/app/views/admin/users/_form.html.slim @@ -3,6 +3,7 @@ = f.input :email, label: 'Email' = f.input :admin = f.input :is_onboarder, label: 'Is Support Consultant' + = f.input :subscribe_email, label: 'Subscribed to Emails?' = f.input :first_name = f.input :last_name = f.input :city diff --git a/db/manifest b/db/manifest index 19a9059d2..3429696ea 100755 --- a/db/manifest +++ b/db/manifest @@ -383,4 +383,5 @@ age_out_sessions.sql alter_crash_dumps.sql onboarding.sql better_lesson_notices.sql -teacher_search_control.sql \ No newline at end of file +teacher_search_control.sql +user_timezone.sql \ No newline at end of file diff --git a/db/up/user_timezone.sql b/db/up/user_timezone.sql new file mode 100644 index 000000000..7f0e0da1d --- /dev/null +++ b/db/up/user_timezone.sql @@ -0,0 +1,2 @@ +ALTER TABLE users ADD COLUMN timezone VARCHAR; +ALTER TABLE users ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb b/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb index 79f5c2c0f..4d6a50682 100644 --- a/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb @@ -52,6 +52,15 @@ module JamRuby subject: options[:subject]) end + def ugly(options) + mail(to: options[:to], + cc: options[:cc], + from: APP_CONFIG.email_generic_from, + body: options[:body], + content_type: "text/plain", + subject: options[:subject]) + end + def recurly_alerts(user, options) body = options[:body] diff --git a/ruby/lib/jam_ruby/models/anonymous_user.rb b/ruby/lib/jam_ruby/models/anonymous_user.rb index e32a230ea..9890a52ba 100644 --- a/ruby/lib/jam_ruby/models/anonymous_user.rb +++ b/ruby/lib/jam_ruby/models/anonymous_user.rb @@ -41,6 +41,10 @@ module JamRuby 0 end + def timezone + @cookies[:'browser.timezone'] + end + def free_jamtracks if has_redeemable_jamtrack 1 diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb index ddd30c2b1..58c4baa38 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking.rb @@ -962,6 +962,7 @@ module JamRuby if lesson_booking.same_school lesson_booking.same_school_free = false # !user.school.education # non-education schools (music schools) are 'free' when school-on-school end + user.update_timezone(lesson_booking_slots[0].timezone) if lesson_booking_slots.length > 0 else lesson_booking.same_school = false lesson_booking.same_school_free = false @@ -975,7 +976,7 @@ module JamRuby end if lesson_booking_slots if lesson_booking.save - description = '' if description.nil? + description = '' if description.nil? msg = ChatMessage.create(user, lesson_booking.lesson_sessions[0], description, ChatMessage::CHANNEL_LESSON, nil, teacher, lesson_booking.lesson_sessions[0], 'Lesson Requested') end end diff --git a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb index 0b190609f..74911ae5f 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb @@ -166,12 +166,14 @@ module JamRuby found ||= lesson_session.lesson_booking end - def pretty_scheduled_start(with_timezone = true) + def pretty_scheduled_start(with_timezone = true, user_tz = nil) start_time = scheduled_time(0) + tz_identifier = user_tz || self.timezone + begin - tz = TZInfo::Timezone.get(timezone) + tz = TZInfo::Timezone.get(tz_identifier) rescue Exception => e @@log.error("unable to find timezone=#{tz_identifier}, e=#{e}") end @@ -205,12 +207,14 @@ module JamRuby end end - def pretty_start_time(with_timezone = true) + def pretty_start_time(with_timezone = true, user_tz = nil) start_time = scheduled_time(0) + tz_identifier = user_tz || self.timezone + begin - tz = TZInfo::Timezone.get(timezone) + tz = TZInfo::Timezone.get(tz_identifier) rescue Exception => e @@log.error("unable to find timezone=#{tz_identifier}, e=#{e}") end diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb index 3251695d8..922bdb854 100644 --- a/ruby/lib/jam_ruby/models/lesson_session.rb +++ b/ruby/lib/jam_ruby/models/lesson_session.rb @@ -984,6 +984,8 @@ module JamRuby #end if self.save + proposer.update_timezone(slot.timezone) if proposer + #if update_all && !lesson_booking.counter(self, proposer, slot) if !lesson_booking.counter(self, proposer, slot) response = lesson_booking diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index d53ba7444..fb4a37d30 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -1215,7 +1215,7 @@ SQL duration end - def pretty_scheduled_start(with_timezone = true, shorter = false) + def pretty_scheduled_start(with_timezone = true, shorter = false, user_tz = nil) if scheduled_start && scheduled_duration start_time = scheduled_start @@ -1224,6 +1224,9 @@ SQL tz_identifier, tz_display = MusicSession.split_timezone(timezone) short_tz = 'GMT' + if user_tz + tz_identifier = user_tz + end begin tz = TZInfo::Timezone.get(tz_identifier) rescue Exception => e diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index baf599bd4..8b7b0083f 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -136,7 +136,7 @@ module JamRuby # notifications has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id" - + # chats has_many :chats, :class_name => "JamRuby::ChatMessage", :foreign_key => "user_id" @@ -350,6 +350,18 @@ module JamRuby end end + def update_timezone(timezone) + if timezone.nil? + return + + end + begin + TZInfo::Timezone.get(timezone) + User.where(id: self.id).update_all(timezone: timezone) + rescue Exception => e + @@log.error("unable to find timezone=#{timezone}, e=#{e}") + end + end def has_any_free_jamtracks has_redeemable_jamtrack || gifted_jamtracks > 0 end @@ -1211,6 +1223,7 @@ module JamRuby origin = options[:origin] test_drive_package_details = options[:test_drive_package] under_13 = options[:under_13] + timezone = options[:timezone] test_drive_package = TestDrivePackage.find_by_name(test_drive_package_details[:name]) if test_drive_package_details @@ -1540,6 +1553,8 @@ module JamRuby end end end + + user.update_timezone(timezone) user.reload if user.id # gift card adding gifted_jamtracks doesn't reflect here until reload user end @@ -1661,6 +1676,14 @@ module JamRuby "#{dir}/#{ERB::Util.url_encode(file)}" end + def permanently_delete + original_email = self.email + + AdminMailer.ugly({to: original_email, cc: 'support@jamkazam.com', subject:'Your account has been deleted!', body: "This will be the last email you receive from JamKazam.\n\nRegards,\nTeam JamKazam"}).deliver_now + + User.where(id: self.id).update_all(encrypted_password: SecureRandom.uuid, email: "deleted+#{SecureRandom.uuid}@jamkazam.com", subscribe_email: false, remember_token: SecureRandom.uuid, deleted: true) + end + def update_avatar(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, aws_bucket) self.updating_avatar = true diff --git a/ruby/spec/jam_ruby/models/lesson_session_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_spec.rb index 9c4b984bc..dba6e68de 100644 --- a/ruby/spec/jam_ruby/models/lesson_session_spec.rb +++ b/ruby/spec/jam_ruby/models/lesson_session_spec.rb @@ -271,19 +271,6 @@ describe LessonSession do slow[1].should eql lesson_session1 end end - describe "least_time_left" do - it "sorts correctly" do - lesson_session1 = normal_lesson(user, teacher, {counter: true, counterer: user}) - lesson_session2 = normal_lesson(user, teacher, {counter: true, counterer: teacher}) # this shouldn't show up - Timecop.travel(Date.today - 5) - lesson_session3 = normal_lesson(user, teacher, {}) - - slow = LessonSession.unscoped.least_time_left - slow.count.should eql 2 - slow[0].should eql lesson_session1 - slow[1].should eql lesson_session3 - end - end describe "remind_counters" do it "finds old requested and pokes teacher" do @@ -472,9 +459,9 @@ describe LessonSession do first_lesson.is_canceled?.should be true second_lesson.is_canceled?.should be true booking.is_canceled?.should be true - first_lesson.student_short_canceled.should be true first_lesson.student_canceled.should be true first_lesson.teacher_canceled.should be false + first_lesson.student_short_canceled.should be true second_lesson.student_short_canceled.should be false second_lesson.student_canceled.should be true second_lesson.teacher_canceled.should be false diff --git a/web/app/assets/javascripts/basic/basic.js b/web/app/assets/javascripts/basic/basic.js index 331325c3c..c36d303b5 100644 --- a/web/app/assets/javascripts/basic/basic.js +++ b/web/app/assets/javascripts/basic/basic.js @@ -6,6 +6,7 @@ //= require jquery.monkeypatch //= require jquery_ujs //= require jquery.cookie +//= require jstz //= require AAC_underscore //= require AAA_Log //= require globals diff --git a/web/app/assets/javascripts/everywhere/everywhere.js b/web/app/assets/javascripts/everywhere/everywhere.js index f815cb65c..016eec279 100644 --- a/web/app/assets/javascripts/everywhere/everywhere.js +++ b/web/app/assets/javascripts/everywhere/everywhere.js @@ -45,8 +45,13 @@ trackScreenChanges(); + setTimezone() }) + function setTimezone() { + $.cookie("browser.timezone", window.jstz.determine().name(), { expires: 365, path: '/' }); + } + $(document).on('JAMKAZAM_READY', function() { // this event is fired when context.app is initialized @@ -99,6 +104,10 @@ function initializeDialogs(app) { + if(!JK.Banner) { + // we don't use dialogs everywhere (yes, ugly) + return + } var backendAlerts = new JK.BackendAlerts(app); backendAlerts.initialize(); diff --git a/web/app/assets/javascripts/landing/landing.js b/web/app/assets/javascripts/landing/landing.js index 0410ed9c3..d44c8eb66 100644 --- a/web/app/assets/javascripts/landing/landing.js +++ b/web/app/assets/javascripts/landing/landing.js @@ -25,6 +25,7 @@ //= require jquery.manageVsts //= require jquery.lessonSessionActions //= require jquery.jamblasterOptions +//= require jstz //= require ResizeSensor //= require AAA_Log //= require AAC_underscore diff --git a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee index 0171cf344..ed74fafbe 100644 --- a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee @@ -464,6 +464,10 @@ UserStore = context.UserStore isSuspended: () -> @state.booking?.status == 'suspended' + isUnconfirmed: () -> + @state.booking?.status == 'unconfirmed' + + isStudentCountered: () -> @counteredSlot()?['is_student_created?'] @@ -774,6 +778,11 @@ UserStore = context.UserStore header = 'This lesson has been suspended' content = @renderTeacherSuspended() + else if @isUnconfirmed() + + header = 'This lesson has been automatically canceled' + content = @renderTeacherUnconfirmed() + return `

diff --git a/web/lib/user_manager.rb b/web/lib/user_manager.rb index 02428f26f..01d6cb02e 100644 --- a/web/lib/user_manager.rb +++ b/web/lib/user_manager.rb @@ -46,6 +46,7 @@ class UserManager < BaseManager origin = options[:origin] test_drive_package = options[:test_drive_package] under_13 = options[:under_13] + timezone = options[:timezone] recaptcha_failed = false unless options[:skip_recaptcha] # allow callers to opt-of recaptcha @@ -102,7 +103,8 @@ class UserManager < BaseManager education_interest: education_interest, origin: origin, test_drive_package: test_drive_package, - under_13: under_13) + under_13: under_13, + timezone: timezone) user end diff --git a/web/spec/features/activate_account_spec.rb b/web/spec/features/activate_account_spec.rb index 4b87b3e58..9e6032ac8 100644 --- a/web/spec/features/activate_account_spec.rb +++ b/web/spec/features/activate_account_spec.rb @@ -20,19 +20,20 @@ describe "Activate Account Card", :js => true, :type => :feature, :capybara_feat amazon_2_free_card.credits.should eql 2 - find('h2', text: 'Activate Account') + find('.instructions', text: 'Paste or enter your promotional code so we can validate it and credit you with 2 free lessons.') fill_in "code", with: amazon_2_free_card.code + find('a.amazon-a-button-text', text: 'Submit Code').trigger(:click) + + find('.success-msg wbr', 'Your code has been validated!') fill_in "email", with: "amzposa1@jamkazam.com" fill_in "password", with: "jam123" - find('.redeem-container ins', visible: false).trigger(:click) + find('.checkbox-input input').trigger(:click) - find('button.redeem-giftcard', text: 'ACTIVATE ACCOUNT').trigger(:click) + find('a.amazon-a-button-text', text: 'Create Account').trigger(:click) - find('.jam-class.all-done span.amount-gifted', text: amazon_2_free_card.credits) - find('.done-action a.go-browse').trigger(:click) - - find('h2', text: 'search teachers') + find('.success-msg', "You're all set!") + sleep 3 user = User.find_by_email("amzposa1@jamkazam.com") amazon_2_free_card.reload amazon_2_free_card.user.should eq(user) @@ -40,29 +41,31 @@ describe "Activate Account Card", :js => true, :type => :feature, :capybara_feat amazon_2_free_card.purchased.should be true user.reload user.jamclass_credits.should eq(amazon_2_free_card.credits) + user.timezone.should_not be_nil end it "validates correctly" do visit '/account/activate/code' - find('h2', text: 'Activate Account') + amazon_2_free_card.credits.should eql 2 - find('button.redeem-giftcard', text: 'ACTIVATE ACCOUNT').trigger(:click) - - find('.errors.active', text: "Email can't be blank") - - find('h2', text: 'Activate Account') + find('.instructions', text: 'Paste or enter your promotional code so we can validate it and credit you with 2 free lessons.') fill_in "code", with: amazon_2_free_card.code + find('a.amazon-a-button-text', text: 'Submit Code').trigger(:click) + + find('.success-msg wbr', 'Your code has been validated!') + find('a.amazon-a-button-text', text: 'Create Account').trigger(:click) + find('.error', text: "Email can't be blank") + fill_in "email", with: "amzpos2@jamkazam.com" fill_in "password", with: "jam123" - find('.redeem-container ins', visible: false).trigger(:click) + find('.checkbox-input input').trigger(:click) - find('button.redeem-giftcard', text: 'ACTIVATE ACCOUNT').trigger(:click) + find('a.amazon-a-button-text', text: 'Create Account').trigger(:click) - find('.done-action a.go-browse').trigger(:click) - - find('h2', text: 'search teachers') + find('.success-msg', "You're all set!") + sleep 3 user = User.find_by_email("amzpos2@jamkazam.com") amazon_2_free_card.reload amazon_2_free_card.user.should eq(user) @@ -79,14 +82,15 @@ describe "Activate Account Card", :js => true, :type => :feature, :capybara_feat it "succeeds" do fast_signin(user1, '/account/activate/code') - find('h2', text: 'Activate Account') + find('.instructions', text: 'Paste or enter your promotional code so we can validate it and credit you with 2 free lessons.') + fill_in "code", with: amazon_2_free_card.code - find('button.redeem-giftcard', text: 'ACTIVATE COUPON CODE').trigger(:click) + find('a.amazon-a-button-text', text: 'Submit Code').trigger(:click) - find('.done-action a.go-browse').trigger(:click) + find('a.amazon-a-button-text', text: 'Apply Credits').trigger(:click) - find('h2', text: 'search teachers') + find('.success-msg', text: "You're all set!") user1.reload amazon_2_free_card.reload @@ -101,19 +105,19 @@ describe "Activate Account Card", :js => true, :type => :feature, :capybara_feat it "validates" do fast_signin(user1, '/account/activate/code') - find('h2', text: 'Activate Account') + find('.instructions', text: 'Paste or enter your promotional code so we can validate it and credit you with 2 free lessons.') - find('button.redeem-giftcard').trigger(:click) + find('.amazon-a-button-text', text: 'Submit Code').trigger(:click) - find('.errors.active', text: "This is not a valid code. Please carefully re-enter the code and try again. If it still does not work, please email us at support@jamkazam.com to report this problem.") + find('.error', text: "Code not valid") fill_in "code", with: amazon_2_free_card.code - find('button.redeem-giftcard').trigger(:click) + find('a.amazon-a-button-text', text: 'Submit Code').trigger(:click) - find('.done-action a.go-browse').trigger(:click) + find('a.amazon-a-button-text', text: 'Apply Credits').trigger(:click) - find('h2', text: 'search teachers') + find('.success-msg', text: "You're all set!") user1.reload amazon_2_free_card.reload @@ -121,6 +125,7 @@ describe "Activate Account Card", :js => true, :type => :feature, :capybara_feat amazon_2_free_card.requires_purchase.should be false amazon_2_free_card.purchased.should be true user1.jamclass_credits.should eq(amazon_2_free_card.credits) + user1.timezone.should_not be_nil end end end diff --git a/web/spec/requests/active_music_sessions_api_spec.rb b/web/spec/requests/active_music_sessions_api_spec.rb index 5adb36164..d797a3ed3 100755 --- a/web/spec/requests/active_music_sessions_api_spec.rb +++ b/web/spec/requests/active_music_sessions_api_spec.rb @@ -158,7 +158,7 @@ describe "Active Music Session API ", :type => :api do it "successful" do put "/api/sessions/#{music_session.id}.json", {:description => "you!", :musician_access => false, :fan_chat => false, :fan_access => false, :approval_required => true}.to_json, "CONTENT_TYPE" => 'application/json' - last_response.status.should eql(204) + last_response.status.should eql(200) get "/api/sessions/#{music_session.id}.json", "CONTENT_TYPE" => 'application/json' updated_session = JSON.parse(last_response.body) updated_session["description"].should == "you!" @@ -170,7 +170,7 @@ describe "Active Music Session API ", :type => :api do it "string boolean value" do put "/api/sessions/#{music_session.id}.json", {:musician_access => "false"}.to_json, "CONTENT_TYPE" => 'application/json' - last_response.status.should eql(204) + last_response.status.should eql(200) get "/api/sessions/#{music_session.id}.json", "CONTENT_TYPE" => 'application/json' updated_session = JSON.parse(last_response.body) updated_session["musician_access"].should be false @@ -184,7 +184,7 @@ describe "Active Music Session API ", :type => :api do it "updated genres" do put "/api/sessions/#{music_session.id}.json", {:genre => "jazz"}.to_json, "CONTENT_TYPE" => 'application/json' - last_response.status.should eql(204) + last_response.status.should eql(200) get "/api/sessions/#{music_session.id}.json", "CONTENT_TYPE" => 'application/json' updated_session = JSON.parse(last_response.body) updated_session["genres"].should == ["Jazz"] @@ -523,8 +523,6 @@ describe "Active Music Session API ", :type => :api do join_request = JSON.parse(last_response.body) - puts "join_request #{join_request}" - # now join_requests should still be empty, because we don't share join_requests to people outside the session login(user2) get '/api/sessions.json' From 314d69e82df7bad7d77a3474dd6b58bca30bcee1 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 17 Feb 2018 10:12:38 -0600 Subject: [PATCH 02/13] onboarding settings done --- admin/Gemfile | 3 + admin/Gemfile.lock | 9 ++ admin/README.md | 5 +- admin/app/admin/onboarders.rb | 92 +++++++++++++++++++ admin/app/admin/onboarding.rb | 8 +- admin/app/assets/javascripts/active_admin.js | 5 + .../assets/javascripts/active_admin.js.coffee | 8 ++ admin/app/helpers/application_helper.rb | 1 - admin/app/helpers/jam_sessions_helper.rb | 1 + db/manifest | 3 +- db/up/onboarder_limit.sql | 1 + 11 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 admin/app/admin/onboarders.rb create mode 100644 db/up/onboarder_limit.sql diff --git a/admin/Gemfile b/admin/Gemfile index 595d94904..29f0e2490 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -83,6 +83,9 @@ gem 'sendgrid_toolkit', '>= 1.1.1' gem 'stripe' gem 'zip-codes' gem 'email_validator' +gem 'best_in_place', github: 'bernat/best_in_place' + + #group :libv8 do # gem 'libv8', "~> 4.5.95" diff --git a/admin/Gemfile.lock b/admin/Gemfile.lock index d029ef2ce..e09bbeb57 100644 --- a/admin/Gemfile.lock +++ b/admin/Gemfile.lock @@ -1,3 +1,11 @@ +GIT + remote: git://github.com/bernat/best_in_place.git + revision: 1b779e60ea912ab9c39d5c999b00c8ac44a8e535 + specs: + best_in_place (3.1.1) + actionpack (>= 3.2) + railties (>= 3.2) + PATH remote: ../db/target/ruby_package specs: @@ -628,6 +636,7 @@ DEPENDENCIES amqp (= 0.9.8) aws-sdk (~> 1) bcrypt-ruby (= 3.0.1) + best_in_place! bootstrap-sass (= 2.0.4) bootstrap-will_paginate (= 0.0.6) bugsnag diff --git a/admin/README.md b/admin/README.md index 503c08107..badfd2200 100644 --- a/admin/README.md +++ b/admin/README.md @@ -11,8 +11,9 @@ Overtime we can add more administrative functions and views, but initially this Examples of: -Button on Show Page of Item: 'Send Client Update Notice' in jam_ruby_artifact_updates.rb +* Button on Show Page of Item: 'Send Client Update Notice' in jam_ruby_artifact_updates.rb +* Batch Updates in View page: onboarding.rb (CurrentlyOnboarding) Stuff that is probably breaky: -activeadmin_addons https://github.com/platanus/activeadmin_addons \ No newline at end of file +activeadmin_addons https://github.com/platanus/activeadmin_addons diff --git a/admin/app/admin/onboarders.rb b/admin/app/admin/onboarders.rb new file mode 100644 index 000000000..2f4174c6d --- /dev/null +++ b/admin/app/admin/onboarders.rb @@ -0,0 +1,92 @@ +ActiveAdmin.register JamRuby::User, :as => 'Onboarder' do + + menu :label => 'Onboarder Settings', :parent => 'JamClass' + + config.sort_order = 'created_at' + config.batch_actions = true + config.per_page = 100 + config.paginate = true + config.filters = false + config.clear_action_items! + batch_action :destroy, false + + scope("All Onboarders", default: true) { |scope| scope.where(is_onboarder: true).order(:created_at) } + + + controller do + active_admin_config.includes.push :onboarding_users + + def update + resource.max_onboardings = params[:jam_ruby_user][:max_onboardings] + resource.save! + success.json {} + end + end + + + + index do + def last_week + @last_week_result ||= calculate_last_week + end + + def calculate_last_week + start_day = Date.today.beginning_of_week(:sunday).yesterday.beginning_of_week(:sunday) + end_day = start_day.end_of_week(:sunday) + result = [start_day, end_day] + result + end + + def this_week + @this_week_result ||= calculate_this_week + end + + def calculate_this_week + start_day = Date.today.beginning_of_week(:sunday) + end_day = start_day.end_of_week(:sunday) + result = [start_day, end_day] + result + end + + def week_display(week) + start_day = week[0] + end_day = week[1] + mmyy = start_day.strftime('%b') + "#{mmyy} #{start_day.day}-#{end_day.day}" + end + + def onboarding_select + array = [] + 100.times do |i| + array.push [i.to_s, i.to_s] + end + array + end + column "Name" do |user| + div do + div do + link_to user.name, user.admin_url + end + div do + user.email + end + end + end + column "Max Onboardings" do |user| + best_in_place user, :max_onboardings, as: :select, url: admin_onboarder_path(user),:collection => onboarding_select + end + column "Current Week #{week_display(this_week)}" do |user| + start_date, last_date = this_week + user.onboarding_users.where(onboarder_assigned_at: start_date..last_date).count + end + column "Previous Week #{week_display(last_week)}" do |user| + start_date, last_date = last_week + user.onboarding_users.where(onboarder_assigned_at: start_date..last_date).count + end + column "Current Student WIP" do |user| + user.onboarding_users.where('onboarding_status = ? OR onboarding_status = ?', User::ONBOARDING_STATUS_ASSIGNED, User::ONBOARDING_STATUS_EMAILED).count + end + end + + +end \ No newline at end of file diff --git a/admin/app/admin/onboarding.rb b/admin/app/admin/onboarding.rb index 0adaf3285..7040629d0 100644 --- a/admin/app/admin/onboarding.rb +++ b/admin/app/admin/onboarding.rb @@ -1,6 +1,6 @@ -ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' do +ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do - menu :label => 'Currently Onboarding', :parent => 'JamClass' + menu :label => 'Onboarder Management', :parent => 'JamClass' config.sort_order = 'created_at desc' config.batch_actions = true @@ -11,7 +11,7 @@ ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' do batch_action :destroy, false batch_action :onboarder, form: -> { { - support_consultant: (User.where(is_onboarder: true).includes(:onboarding_users).map {|user| ["#{user.name} (#{user.onboarding_users.length})", user.id]}).to_a.unshift(['Unassign', '']) + support_consultant: (User.where(is_onboarder: true).includes(:onboarding_users).map {|user| ["#{user.name} (#{user.onboarding_users.where('onboarding_status = ? OR onboarding_status = ?', User::ONBOARDING_STATUS_ASSIGNED, User::ONBOARDING_STATUS_EMAILED).count})", user.id]}).to_a.unshift(['Unassign', '']) } } do |ids, inputs| onboarder = inputs[:support_consultant] if onboarder.blank? @@ -59,7 +59,7 @@ ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' do column "Escalated Reason", :onboarding_escalation_reason column "Support Consultant" do |user| if user.onboarder - link_to "#{user.onboarder.name} (#{user.onboarder.onboarding_users.count})", user.onboarder.admin_url + link_to "#{user.onboarder.name} (#{user.onboarder.onboarding_users.where('onboarding_status = ? OR onboarding_status = ?', User::ONBOARDING_STATUS_ASSIGNED, User::ONBOARDING_STATUS_EMAILED).count})", user.onboarder.admin_url else end end diff --git a/admin/app/assets/javascripts/active_admin.js b/admin/app/assets/javascripts/active_admin.js index 84a187b5a..df9db3400 100644 --- a/admin/app/assets/javascripts/active_admin.js +++ b/admin/app/assets/javascripts/active_admin.js @@ -12,3 +12,8 @@ // //= require autocomplete-rails //= require base //= require_tree . + + +$(document).ready(function() { + jQuery(".best_in_place").best_in_place() +}) diff --git a/admin/app/assets/javascripts/active_admin.js.coffee b/admin/app/assets/javascripts/active_admin.js.coffee index e211bdfe7..7d9d7acbd 100644 --- a/admin/app/assets/javascripts/active_admin.js.coffee +++ b/admin/app/assets/javascripts/active_admin.js.coffee @@ -1,2 +1,10 @@ #= require active_admin/base #= require jquery3 +#= require best_in_place +#= require jquery.purr +#= require best_in_place.purr + +$(document).ready -> + # IS NOT HAPPENING: USE ACTIVE_ADMIN.JS + console.log("DAT COFFEE INIT") + jQuery(".best_in_place").best_in_place() \ No newline at end of file diff --git a/admin/app/helpers/application_helper.rb b/admin/app/helpers/application_helper.rb index 80731359d..6e9385e59 100644 --- a/admin/app/helpers/application_helper.rb +++ b/admin/app/helpers/application_helper.rb @@ -1,5 +1,4 @@ module ApplicationHelper - end diff --git a/admin/app/helpers/jam_sessions_helper.rb b/admin/app/helpers/jam_sessions_helper.rb index bf244b019..9ef3f0789 100644 --- a/admin/app/helpers/jam_sessions_helper.rb +++ b/admin/app/helpers/jam_sessions_helper.rb @@ -1,2 +1,3 @@ module JamSessionsHelper + end \ No newline at end of file diff --git a/db/manifest b/db/manifest index 3429696ea..85801522a 100755 --- a/db/manifest +++ b/db/manifest @@ -384,4 +384,5 @@ alter_crash_dumps.sql onboarding.sql better_lesson_notices.sql teacher_search_control.sql -user_timezone.sql \ No newline at end of file +user_timezone.sql +onboarder_limit.sql \ No newline at end of file diff --git a/db/up/onboarder_limit.sql b/db/up/onboarder_limit.sql new file mode 100644 index 000000000..2a2e35ba9 --- /dev/null +++ b/db/up/onboarder_limit.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN max_onboardings INTEGER NOT NULL DEFAULT 0; From 13f22abaa858da49e60c4a499943674215ad7ce0 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 22 Feb 2018 16:33:34 -0600 Subject: [PATCH 03/13] Best_inPlace no git --- admin/Gemfile | 2 +- admin/Gemfile.lock | 13 ++++--------- admin/app/admin/dashboard.rb | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/admin/Gemfile b/admin/Gemfile index 29f0e2490..be68523a5 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -83,7 +83,7 @@ gem 'sendgrid_toolkit', '>= 1.1.1' gem 'stripe' gem 'zip-codes' gem 'email_validator' -gem 'best_in_place', github: 'bernat/best_in_place' +gem 'best_in_place' #, github: 'bernat/best_in_place' diff --git a/admin/Gemfile.lock b/admin/Gemfile.lock index e09bbeb57..a946d9be3 100644 --- a/admin/Gemfile.lock +++ b/admin/Gemfile.lock @@ -1,11 +1,3 @@ -GIT - remote: git://github.com/bernat/best_in_place.git - revision: 1b779e60ea912ab9c39d5c999b00c8ac44a8e535 - specs: - best_in_place (3.1.1) - actionpack (>= 3.2) - railties (>= 3.2) - PATH remote: ../db/target/ruby_package specs: @@ -103,6 +95,9 @@ GEM backports (3.11.1) bcrypt (3.1.11) bcrypt-ruby (3.0.1) + best_in_place (3.1.1) + actionpack (>= 3.2) + railties (>= 3.2) binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) bootstrap-sass (2.0.4.0) @@ -636,7 +631,7 @@ DEPENDENCIES amqp (= 0.9.8) aws-sdk (~> 1) bcrypt-ruby (= 3.0.1) - best_in_place! + best_in_place bootstrap-sass (= 2.0.4) bootstrap-will_paginate (= 0.0.6) bugsnag diff --git a/admin/app/admin/dashboard.rb b/admin/app/admin/dashboard.rb index 51903b216..f4d8820f6 100644 --- a/admin/app/admin/dashboard.rb +++ b/admin/app/admin/dashboard.rb @@ -9,7 +9,7 @@ ActiveAdmin.register_page "Dashboard" do small ul do li link_to "Users", admin_users_path li link_to "Teachers", admin_teachers_path - li link_to "Onboarding Management", admin_currently_onboardings_path + li link_to "Onboarding Management", admin_onboarder_managements_path li link_to "Lesson Sessions", admin_lesson_sessions_path li link_to "Slow Lesson Responses", admin_slow_responses_path From fce2c68f4f2389f02739d1b804c6ff04cb123b3b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 25 Feb 2018 16:28:12 -0600 Subject: [PATCH 04/13] engagement emails --- admin/app/admin/onboarders.rb | 1 - admin/app/admin/onboarding.rb | 47 +++- db/manifest | 3 +- db/up/onboarding_emails.sql | 24 ++ ruby/lib/jam_ruby/app/mailers/user_mailer.rb | 51 ++++ .../take_first_free_lesson.html.erb | 45 ++++ .../take_first_free_lesson.text.erb | 26 +++ .../user_mailer/take_paid_lesson.html.erb | 45 ++++ .../user_mailer/take_paid_lesson.text.erb | 28 +++ .../take_second_free_lesson.html.erb | 29 +++ .../take_second_free_lesson.text.erb | 24 ++ ruby/lib/jam_ruby/models/lesson_booking.rb | 3 +- ruby/lib/jam_ruby/models/lesson_session.rb | 75 ++++-- ruby/lib/jam_ruby/models/user.rb | 47 +++- .../jam_ruby/models/lesson_session_spec.rb | 220 ++++++++++++++++++ ruby/spec/mailers/render_emails_spec.rb | 107 +++++++++ 16 files changed, 755 insertions(+), 20 deletions(-) create mode 100644 db/up/onboarding_emails.sql create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.html.erb create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.text.erb create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.html.erb create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.text.erb create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.html.erb create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.text.erb diff --git a/admin/app/admin/onboarders.rb b/admin/app/admin/onboarders.rb index 2f4174c6d..bb84642bb 100644 --- a/admin/app/admin/onboarders.rb +++ b/admin/app/admin/onboarders.rb @@ -12,7 +12,6 @@ ActiveAdmin.register JamRuby::User, :as => 'Onboarder' do scope("All Onboarders", default: true) { |scope| scope.where(is_onboarder: true).order(:created_at) } - controller do active_admin_config.includes.push :onboarding_users diff --git a/admin/app/admin/onboarding.rb b/admin/app/admin/onboarding.rb index 7040629d0..33eb7420e 100644 --- a/admin/app/admin/onboarding.rb +++ b/admin/app/admin/onboarding.rb @@ -41,7 +41,11 @@ ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do filter :onboarder, as: :select, :collection => User.where(is_onboarder: true), label: 'Support Consultant' filter :onboarder_id_blank, :as => :boolean, label: 'Unassigned' filter :onboarding_escalation_reason_present, :as => :boolean, label: 'Escalated' - scope("TestDrive/Amazon Users", default: true) { |scope| scope.joins(:posa_cards).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) } + scope("Unassigned", default: true) { |scope| scope.joins(:posa_cards).where(onboarding_status: User::ONBOARDING_STATUS_UNASSIGNED).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) } + scope("Escalated") { |scope| scope.joins(:posa_cards).where('onboarding_status = ?', User::ONBOARDING_STATUS_ESCALATED).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) } + scope("Needs Manual Email") { |scope| scope.joins(:posa_cards).where(onboarding_status: User::ONBOARDING_STATUS_ONBOARDED).where('(stuck_take_flesson = TRUE AND sent_admin_take_flesson_email_at is NULL) OR (stuck_take_2nd_flesson = TRUE AND sent_admin_take_2nd_flesson_email_at IS NULL) OR (stuck_take_plesson = TRUE AND sent_admin_take_plesson_email_at IS NULL)').where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) } + scope("Assigned") { |scope| scope.joins(:posa_cards).where('onboarding_status = ? OR onboarding_status = ?', User::ONBOARDING_STATUS_ASSIGNED, User::ONBOARDING_STATUS_EMAILED).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) } + scope("All TestDrive/Amazon Users") { |scope| scope.joins(:posa_cards).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) } controller do @@ -63,6 +67,19 @@ ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do else end end + column "Manual Email Needed" do |user| + + div do + if user.stuck_take_plesson + link_to("sent take paid lesson email", mark_sent_paid_lesson_admin_onboarder_management_path(user.id), {'data-confirm': "You sent an email manually to the user to remind them to take a paid lesson?"}) + elsif user.stuck_take_2nd_flesson + link_to("sent take 2nd lesson email", mark_sent_2nd_free_lesson_admin_onboarder_management_path(user.id), {'data-confirm': "You sent an email manually to the user to remind them to take 2nd free lesson?"}) + elsif user.stuck_take_flesson + link_to("sent take 1st lesson email", mark_sent_1st_free_lesson_admin_onboarder_management_path(user.id), {'data-confirm': "You sent an email manually to the user to remind them to take a 1st free lesson?"}) + else + end + end + end column "Signup" do |user| user.created_at.to_date @@ -74,6 +91,14 @@ ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do column "Email 4", :onboarding_email_4_sent_at column "Email 5", :onboarding_email_5_sent_at column "Test Session", :onboarding_test_session_scheduled_at + column "Admin Actions" do |user| + div do + if user.is_waiting_onboarding || user.is_onboarding + link_to("mark onboarded", mark_onboarded_admin_onboarder_management_path(user.id), { 'data-confirm': "Mark onboarded?"}) + end + end + end + column "Onboarder Notes", :onboarding_onboarder_notes end member_action :update_onboarder, :method => :post do @@ -82,4 +107,24 @@ ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do redirect_to :back end + member_action :mark_sent_paid_lesson, :method => :get do + resource.mark_sent_paid_lesson + redirect_to :back + end + + member_action :mark_sent_2nd_free_lesson, :method => :get do + resource.mark_sent_2nd_free_lesson + redirect_to :back + end + + member_action :mark_sent_1st_free_lesson, :method => :get do + resource.mark_sent_1st_free_lesson + redirect_to :back + end + + member_action :mark_onboarded, :method => :get do + resource.mark_onboarded + redirect_to :back + end + end \ No newline at end of file diff --git a/db/manifest b/db/manifest index 85801522a..ece0a3d84 100755 --- a/db/manifest +++ b/db/manifest @@ -385,4 +385,5 @@ onboarding.sql better_lesson_notices.sql teacher_search_control.sql user_timezone.sql -onboarder_limit.sql \ No newline at end of file +onboarder_limit.sql +onboarding_emails.sql \ No newline at end of file diff --git a/db/up/onboarding_emails.sql b/db/up/onboarding_emails.sql new file mode 100644 index 000000000..e2312537f --- /dev/null +++ b/db/up/onboarding_emails.sql @@ -0,0 +1,24 @@ +ALTER TABLE users ADD COLUMN sent_take_flesson_email_at TIMESTAMP WITHOUT TIME ZONE; +ALTER TABLE users ADD COLUMN sent_take_flesson_email_times INTEGER NOT NULL DEFAULT 0; +ALTER TABLE users ADD COLUMN sent_take_2nd_flesson_email_at TIMESTAMP WITHOUT TIME ZONE; +ALTER TABLE users ADD COLUMN sent_take_2nd_flesson_email_times INTEGER NOT NULL DEFAULT 0; +ALTER TABLE users ADD COLUMN sent_take_plesson_email_at TIMESTAMP WITHOUT TIME ZONE; +ALTER TABLE users ADD COLUMN sent_take_plesson_email_times INTEGER NOT NULL DEFAULT 0; +ALTER TABLE users ADD COLUMN second_onboarding_free_lesson_at timestamp without time zone; +ALTER TABLE users ADD COLUMN sent_admin_take_flesson_email_at TIMESTAMP WITHOUT TIME ZONE; +ALTER TABLE users ADD COLUMN sent_admin_take_2nd_flesson_email_at TIMESTAMP WITHOUT TIME ZONE; +ALTER TABLE users ADD COLUMN sent_admin_take_plesson_email_at TIMESTAMP WITHOUT TIME ZONE; +ALTER TABLE users ADD COLUMN stuck_take_flesson BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE users ADD COLUMN stuck_take_2nd_flesson BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE users ADD COLUMN stuck_take_plesson BOOLEAN NOT NULL DEFAULT FALSE; + +CREATE INDEX index_first_onboarding_paid_lesson_at ON users USING btree(first_onboarding_paid_lesson_at); +CREATE INDEX index_onboarding_onboarded_at ON users USING btree(onboarding_onboarded_at); +CREATE INDEX index_onboarding_status ON users USING btree(onboarding_status); +CREATE INDEX index_stuck_take_plesson ON users USING btree(stuck_take_plesson); +CREATE INDEX index_stuck_take_2nd_flesson ON users USING btree(stuck_take_2nd_flesson); +CREATE INDEX index_stuck_take_flesson ON users USING btree(stuck_take_flesson); +CREATE INDEX index_sent_admin_take_flesson_email_at ON users USING btree(sent_admin_take_flesson_email_at); +CREATE INDEX index_sent_admin_take_2nd_flesson_email_at ON users USING btree(sent_admin_take_2nd_flesson_email_at); +CREATE INDEX index_sent_admin_take_plesson_email_at ON users USING btree(sent_admin_take_plesson_email_at); +CREATE INDEX index_posa_cards_lesson_package_type_id ON posa_cards USING btree(lesson_package_type_id); diff --git a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb index 296f75580..b10b6a22e 100644 --- a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb @@ -49,6 +49,57 @@ module JamRuby end end + def take_paid_lesson(user) + @user = user + @lesson = user.taken_lessons.order(:created_at).last + @teacher = @lesson.teacher + + @subject = "Time to get even better at your instrument" + sendgrid_category "promo_lesson_reminder" + sendgrid_unique_args :type => "promo_lesson_reminder" + + sendgrid_recipients([user.email]) + sendgrid_substitute('@USERID', [user.id]) + + mail(:to => user.email, :subject => @subject) do |format| + format.text + format.html + end + end + + def take_second_free_lesson(user) + @user = user + @lesson = user.taken_lessons.order(:created_at).last + @teacher = @lesson.teacher + + @subject = "A reminder to take your second free lesson" + sendgrid_category "promo_lesson_reminder" + sendgrid_unique_args :type => "promo_lesson_reminder" + + sendgrid_recipients([user.email]) + sendgrid_substitute('@USERID', [user.id]) + + mail(:to => user.email, :subject => @subject) do |format| + format.text + format.html + end + end + + def take_first_free_lesson(user) + @user = user + @subject = "A reminder to take your first free lesson" + sendgrid_category "promo_lesson_reminder" + sendgrid_unique_args :type => "promo_lesson_reminder" + + sendgrid_recipients([user.email]) + sendgrid_substitute('@USERID', [user.id]) + + mail(:to => user.email, :subject => @subject) do |format| + format.text + format.html + end + end + def student_education_welcome_message(user) @user = user @subject = "Welcome to JamKazam and JamClass online lessons!" diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.html.erb new file mode 100644 index 000000000..b3055afbf --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.html.erb @@ -0,0 +1,45 @@ +<% provide(:title, @subject) %> + + +<% if !@user.anonymous? %> +

Hi <%= @user.first_name %>, +

+<% end %> + +<% if @user.sent_take_flesson_email_times == 0 %> + +

We hope you had a great support experience with the JamKazam consultant who prepared you to get into your first free online lesson. Life gets busy. So before you forget about it, please find an instructor who looks great for you, and book your first free lesson now! +

+ +

+ Here is an article that explains how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your first lesson: http://bit.ly/2k3qNMT. +

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+ + <% elsif @user.sent_take_flesson_email_times == 1 %> + +

We noticed you haven't booked your first free lesson yet. Don't forget to take advantage of this amazing limited promotion. Your two free lessons are valued at approximately $60! Book your first lesson today.

+

+ Here is an article that explains how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your first lesson: http://bit.ly/2k3qNMT. +

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+ +<% else %> + + +

It looks like you're busy, but we wanted to remind you once more to book your first free lesson before you forget and let this terrific promotion get away. Start your musical journey with a world-class instructor who can help you achieve your goals. There is no better way to learn and master your new instrument!

+

+ Here is an article that explains how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your first lesson: http://bit.ly/2k3qNMT. +

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+ + <% end %> + +

Best Regards,
+ Team JamKazam

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.text.erb new file mode 100644 index 000000000..869994f72 --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_first_free_lesson.text.erb @@ -0,0 +1,26 @@ +<% if !@user.anonymous? %> +Hi <%= @user.first_name %>, +<% end %> + +<% if @user.sent_take_flesson_email_times == 0 %> +We hope you had a great support experience with the JamKazam consultant who prepared you to get into your first free online lesson. Life gets busy. So before you forget about it, please find an instructor who looks great for you, and book your first free lesson now! + +Here is an article that explains how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your first lesson:http://bit.ly/2k3qNMT. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% elsif @user.sent_take_flesson_email_times == 1 %> +We noticed you haven't booked your first free lesson yet. Don't forget to take advantage of this amazing limited promotion. Your two free lessons are valued at approximately $60! Book your first lesson today. + +Here is an article that explains how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your first lesson:http://bit.ly/2k3qNMT. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% else %> +It looks like you're busy, but we wanted to remind you once more to book your first free lesson before you forget and let this terrific promotion get away. Start your musical journey with a world-class instructor who can help you achieve your goals. There is no better way to learn and master your new instrument! + +Here is an article that explains how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your first lesson:http://bit.ly/2k3qNMT. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% end %> + +Best Regards, +Team JamKazam \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.html.erb new file mode 100644 index 000000000..0979d40fc --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.html.erb @@ -0,0 +1,45 @@ +<% provide(:title, @subject) %> + + +<% if !@user.anonymous? %> +

Hi <%= @user.first_name %>, +

+<% end %> + +<% if @user.sent_take_plesson_email_times == 0 %> +

+ Now that you've enjoyed your first two lessons free, continue down the musical path you've started with your instructor. Schedule recurring weekly lessons for the best results and fastest progress, or schedule lessons one at a time. It's up to you. +

+

+ You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. +

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+ <% elsif @user.sent_take_plesson_email_times == 1 %> +

+ You're off to a great start with your music teacher. Don't lose momentum! +

+

+ You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. +

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+ <% else %> +

+ The best way - by far - to master your instrument and reach your musical goals is to have a great instructor - someone who can guide you, build the right foundation and technique from the beginning, inspire you, and make your practice pay off with the greatest return on your investment of time. If you want to play well, stay on the path with your instructor. +

+

+ You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. +

+

+ Or if you need to find a different instructor for any reason, here's an article on how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your lesson: http://bit.ly/2k3qNMT. +

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+ <% end %> + +

Best Regards,
+ Team JamKazam

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.text.erb new file mode 100644 index 000000000..935699c18 --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_paid_lesson.text.erb @@ -0,0 +1,28 @@ +<% if !@user.anonymous? %> +Hi <%= @user.first_name %>, +<% end %> + +<% if @user.sent_take_plesson_email_times == 0 %> +Now that you've enjoyed your first two lessons free, continue down the musical path you've started with your instructor. Schedule recurring weekly lessons for the best results and fastest progress, or schedule lessons one at a time. It's up to you. + +You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% elsif @user.sent_take_plesson_email_times == 1 %> +You're off to a great start with your music teacher. Don't lose momentum! + +You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% else %> +The best way - by far - to master your instrument and reach your musical goals is to have a great instructor - someone who can guide you, build the right foundation and technique from the beginning, inspire you, and make your practice pay off with the greatest return on your investment of time. If you want to play well, stay on the path with your instructor. + +You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. + +Or if you need to find a different instructor for any reason, here's an article on how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your lesson: http://bit.ly/2k3qNMT. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% end %> + +Best Regards, +Team JamKazam diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.html.erb new file mode 100644 index 000000000..bca989c40 --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.html.erb @@ -0,0 +1,29 @@ +<% provide(:title, @subject) %> + + +<% if !@user.anonymous? %> +

Hi <%= @user.first_name %>, +

+<% end %> + +<% if @user.sent_take_2nd_flesson_email_times == 0 %> +

We hope you had a great first music lesson with <%= @teacher.name %>. Schedule your second free lesson with this instructor now at this page: <%= @teacher.teacher_profile_url %>, and continue your musical journey!

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+<% elsif @user.sent_take_2nd_flesson_email_times == 1 %> +

Just a quick reminder to take advantage of your second free lesson, and book it today! Schedule your second lesson with the same instructor from your first lesson at this page: <%= @teacher.teacher_profile_url %>.

+

Or if you didn't love your instructor for any reason, you can search for a different instructor for your second free lesson. Here's the article on how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your lesson: http://bit.ly/2k3qNMT.

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+<% else %> +

We're sending you one last reminder to book your second free lesson. Don't put it off to later and forget! A great instructor will make a huge difference in the progress you make in mastering your new instrument. You can book another lesson with your first instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>.

+

Or if you didn't love your instructor for any reason, you can search for a different instructor for your second free lesson. Here's the article on how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your lesson: http://bit.ly/2k3qNMT.

+

+ If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +

+<% end %> + +

Best Regards,
+ Team JamKazam

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.text.erb new file mode 100644 index 000000000..115ee401a --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/take_second_free_lesson.text.erb @@ -0,0 +1,24 @@ +<% if !@user.anonymous? %> +Hi <%= @user.first_name %>, +<% end %> + +<% if @user.sent_take_2nd_flesson_email_times == 0 %> +We hope you had a great first music lesson with <%= @teacher.name %>. Schedule your second free lesson with this instructor now at this page: <%= @teacher.teacher_profile_url %>, and continue your musical journey! + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% elsif @user.sent_take_2nd_flesson_email_times == 1 %> +Just a quick reminder to take advantage of your second free lesson, and book it today! Schedule your second lesson with the same instructor from your first lesson at this page: <%= @teacher.teacher_profile_url %>. + +Or if you didn't love your instructor for any reason, you can search for a different instructor for your second free lesson. Here's the article on how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your lesson: http://bit.ly/2k3qNMT. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% else %> +We're sending you one last reminder to book your second free lesson. Don't put it off to later and forget! A great instructor will make a huge difference in the progress you make in mastering your new instrument. You can book another lesson with your first instructor at his/her profile page here: <%= @teacher.teacher_profile_url %>. + +Or if you didn't love your instructor for any reason, you can search for a different instructor for your second free lesson. Here's the article on how to search for your ideal teacher: http://bit.ly/2kBPXBz. And here is an article that explains how to book your lesson: http://bit.ly/2k3qNMT. + +If you have any trouble, please email us at support@jamkazam.com. Or you can call us at 877-376-8742. +<% end %> + +Best Regards, +Team JamKazam \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb index 58c4baa38..758c94710 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking.rb @@ -840,7 +840,8 @@ module JamRuby if !user.has_test_drives? && !user.can_buy_test_drive? errors.add(:user, "have no remaining test drives") elsif teacher.has_booked_test_drive_with_student?(user) && !user.admin - errors.add(:user, "have an in-progress or successful TestDrive with this teacher already") + # we don't want to restrict this anymore. just let'em go to same teacher + # errors.add(:user, "have an in-progress or successful TestDrive with this teacher already") end end diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb index 922bdb854..83a2f116c 100644 --- a/ruby/lib/jam_ruby/models/lesson_session.rb +++ b/ruby/lib/jam_ruby/models/lesson_session.rb @@ -94,11 +94,11 @@ module JamRuby end def teacher_distribution - teacher_distributions.where(education:false).where('retailer_id is null').first + teacher_distributions.where(education: false).where('retailer_id is null').first end def education_distribution - teacher_distributions.where(education:true).first + teacher_distributions.where(education: true).first end def retailer_distribution @@ -131,6 +131,7 @@ module JamRuby complete_sessions remind_counters remind_counters_recurring + onboarding_email_reminders end def self.minutely_check @@ -144,7 +145,7 @@ module JamRuby .where("? > (COALESCE(lesson_sessions.sent_counter_reminder_at, lesson_sessions.countered_at, lesson_bookings.sent_notices_at) + (INTERVAL '24 hours'))", Time.now).each do |music_session| lesson_session = music_session.lesson_session if lesson_session.student_last_proposed? - relevant_time = [lesson_session.countered_at, lesson_session.lesson_booking.sent_notices_at].find{|x|!x.nil?} + relevant_time = [lesson_session.countered_at, lesson_session.lesson_booking.sent_notices_at].find { |x| !x.nil? } UserMailer.student_no_comm_other(lesson_session.lesson_booking, relevant_time < Time.now - 3.days).deliver_now UserMailer.teacher_counter_reminder(lesson_session).deliver_now @@ -159,7 +160,7 @@ module JamRuby def self.remind_counters_recurring MusicSession.joins(lesson_session: :lesson_booking) - .where("lesson_bookings.status = ? AND lesson_bookings.sent_counter_reminder = false AND lesson_bookings.recurring = TRUE",LessonBooking::STATUS_COUNTERED) + .where("lesson_bookings.status = ? AND lesson_bookings.sent_counter_reminder = false AND lesson_bookings.recurring = TRUE", LessonBooking::STATUS_COUNTERED) .where("? > (COALESCE(lesson_sessions.countered_at, lesson_bookings.sent_notices_at) + (INTERVAL '24 hours'))", Time.now).each do |music_session| lesson_session = music_session.lesson_session if lesson_session.student_last_proposed? @@ -172,6 +173,51 @@ module JamRuby end end + def self.remindable_onboarding_users + User.where('first_onboarding_paid_lesson_at IS NULL AND onboarding_onboarded_at IS NOT NULL') + .where('stuck_take_flesson = FALSE and stuck_take_2nd_flesson = FALSE and stuck_take_plesson = FALSE') + end + + def self.onboarding_email_reminders + remindable_onboarding_users.each do |user| + if user.second_onboarding_free_lesson_at && user.sent_take_plesson_email_times < 4 + + last_sent = user.sent_take_plesson_email_at || user.second_onboarding_free_lesson_at + if last_sent < 2.days.ago + if user.sent_take_plesson_email_times == 3 + User.update_all(id: user.id, stuck_take_plesson: true) + else + UserMailer.take_paid_lesson(user).deliver_now + User.update_all(id: user.id, sent_take_plesson_email_times: user.sent_take_plesson_email_times + 1, sent_take_plesson_email_at: Time.now) + end + end + + elsif user.first_onboarding_free_lesson_at && user.sent_take_2nd_flesson_email_times < 4 + last_sent = user.sent_take_2nd_flesson_email_at || user.first_onboarding_free_lesson_at + if last_sent < 2.days.ago + if user.sent_take_2nd_flesson_email_times == 3 + User.update_all(id: user.id, stuck_take_2nd_flesson: true) + else + UserMailer.take_second_free_lesson(user).deliver_now + User.update_all(id: user.id, sent_take_2nd_flesson_email_times: user.sent_take_2nd_flesson_email_times + 1, sent_take_2nd_flesson_email_at: Time.now) + end + end + + elsif user.sent_take_flesson_email_times < 4 + last_sent = user.sent_take_flesson_email_at || user.onboarding_onboarded_at + if last_sent < 2.days.ago + if user.sent_take_flesson_email_times == 3 + User.update_all(id: user.id, stuck_take_flesson: true) + else + UserMailer.take_first_free_lesson(user).deliver_now + User.update_all(id: user.id, sent_take_flesson_email_times: user.sent_take_flesson_email_times + 1, sent_take_flesson_email_at: Time.now) + end + end + end + end + end + + # give 2 days to auto-cancel; this lets people just jump in a session and mark it done def self.auto_cancel MusicSession.joins(lesson_session: :lesson_booking).where('lesson_sessions.status = ? OR lesson_bookings.status = ?', LessonSession::STATUS_REQUESTED, LessonBooking::STATUS_COUNTERED).where("? > scheduled_start + (INTERVAL '2 days') + (INTERVAL '1 minutes' * (duration))", Time.now).each do |music_session| @@ -319,15 +365,16 @@ module JamRuby if student.first_onboarding_free_lesson_at.nil? student.first_onboarding_free_lesson_at = Time.now student.save + elsif student.second_onboarding_free_lesson_at.nil? + student.second_onboarding_free_lesson_at = Time.now + student.save end else if student.first_onboarding_paid_lesson_at.nil? student.first_onboarding_paid_lesson_at = Time.now student.save end - end - end mark_lesson(analysis[:bill]) @@ -399,6 +446,7 @@ module JamRuby student end end + def session_completed LessonSession.transaction do self.lock! @@ -473,7 +521,7 @@ module JamRuby if !sent_notices if success - teacher_distributions.update_all(ready:true) # possibly there are 0 distributions on this lesson + teacher_distributions.update_all(ready: true) # possibly there are 0 distributions on this lesson student.test_drive_succeeded(self) else student.test_drive_failed(self) @@ -1137,6 +1185,7 @@ module JamRuby 'TBD' end end + def timed_description if is_test_drive? "TestDrive session with #{self.lesson_booking.student.name} on #{time_for_admin}" @@ -1151,11 +1200,11 @@ module JamRuby def cancel_by_admin self.cancel({ - canceler: nil, - canceled_by_admin: true, - message: 'Canceled by JamKazam administrator', - update_all: false - }) + canceler: nil, + canceled_by_admin: true, + message: 'Canceled by JamKazam administrator', + update_all: false + }) end def intervened @@ -1178,7 +1227,7 @@ module JamRuby end def last_response_time - [countered_at, lesson_booking.sent_notices_at].find{|x|!x.nil?} + [countered_at, lesson_booking.sent_notices_at].find { |x| !x.nil? } end def stripe_description(lesson_booking) diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 8b7b0083f..535eaae81 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -38,7 +38,7 @@ module JamRuby ONBOARDING_STATUS_ONBOARDED = "Onboarded" ONBOARDING_STATUS_LOST = "Lost" ONBOARDING_STATUS_ESCALATED = "Escalated" - ONBOARDING_STATUS_FREE_LESSON = "Free Lesson Taken" + ONBOARDING_STATUS_FREE_LESSON = "One Free Lesson Taken" ONBOARDING_STATUS_PAID_LESSON = "Paid Lesson Taken" ONBOARDING_STATUES = [ONBOARDING_STATUS_UNASSIGNED, ONBOARDING_STATUS_ASSIGNED, ONBOARDING_STATUS_EMAILED, ONBOARDING_STATUS_ONBOARDED, ONBOARDING_STATUS_LOST, ONBOARDING_STATUS_ESCALATED, ONBOARDING_STATUS_FREE_LESSON, ONBOARDING_STATUS_PAID_LESSON ] SESSION_OUTCOME_SUCCESSFUL = "Successful" @@ -328,8 +328,18 @@ module JamRuby onboarding_email_5_sent_at_changed? || first_onboarding_free_lesson_at_changed? || first_onboarding_paid_lesson_at_changed? || + second_onboarding_free_lesson_at? || onboarding_onboarded_at - User.where(id: self.id).update_all(onboarding_status: self.computed_onboarding_status) + + updates = {onboarding_status: self.computed_onboarding_status} + + if first_onboarding_free_lesson_at_changed? || first_onboarding_paid_lesson_at_changed? || second_onboarding_free_lesson_at? + updates[:stuck_take_flesson] = false + updates[:stuck_take_2nd_flesson] = false + updates[:stuck_take_plesson] = false + end + + User.where(id: self.id).update_all(updates) end end def update_teacher_pct @@ -338,6 +348,14 @@ module JamRuby end end + def is_waiting_onboarding + ONBOARDING_STATUS_UNASSIGNED + end + + def is_onboarding + ONBOARDING_STATUS_ASSIGNED || ONBOARDING_STATUS_ESCALATED || ONBOARDING_STATUS_EMAILED || ONBOARDING_STATUS_LOST + end + def user_progression_fields @user_progression_fields ||= Set.new ["first_downloaded_client_at", "first_ran_client_at", "first_music_session_at", "first_real_music_session_at", "first_good_music_session_at", "first_certified_gear_at", "first_invited_at", "first_friended_at", "first_recording_at", "first_social_promoted_at", "first_played_jamtrack_at"] end @@ -2502,6 +2520,29 @@ module JamRuby self.save! end + def mark_sent_paid_lesson + self.stuck_take_plesson = false + self.sent_admin_take_plesson_email_at = Time.now + self.save! + end + + def mark_sent_2nd_free_lesson + self.stuck_take_2nd_flesson = false + self.sent_admin_take_2nd_flesson_email_at = Time.now + self.save! + end + + def mark_sent_1st_free_lesson + self.stuck_take_flesson = false + self.sent_admin_take_flesson_email_at = Time.now + self.save! + end + + def mark_onboarded + self.onboarding_onboarded_at = Time.now + self.save! + end + def has_booked_with_student?(student, since_at = nil) LessonBooking.engaged_bookings(student, self, since_at).count > 0 end @@ -2517,7 +2558,7 @@ module JamRuby def computed_onboarding_status if first_onboarding_paid_lesson_at ONBOARDING_STATUS_PAID_LESSON - elsif first_onboarding_free_lesson_at + elsif second_onboarding_free_lesson_at || first_onboarding_free_lesson_at ONBOARDING_STATUS_FREE_LESSON elsif onboarding_onboarded_at ONBOARDING_STATUS_ONBOARDED diff --git a/ruby/spec/jam_ruby/models/lesson_session_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_spec.rb index dba6e68de..c7d0bbfbf 100644 --- a/ruby/spec/jam_ruby/models/lesson_session_spec.rb +++ b/ruby/spec/jam_ruby/models/lesson_session_spec.rb @@ -504,6 +504,226 @@ describe LessonSession do end end + describe "remindable_onboarding_users" do + it "works" do + LessonSession.remindable_onboarding_users.count.should eql 0 + + user.onboarding_onboarded_at = Time.now + user.first_onboarding_paid_lesson_at = nil + user.save! + + LessonSession.remindable_onboarding_users.count.should eql 1 + + user.sent_take_plesson_email_times = 3 + user.sent_take_2nd_flesson_email_times = 3 + user.sent_take_plesson_email_times = 3 + user.save! + + LessonSession.remindable_onboarding_users.count.should eql 0 + end + end + + describe "onboarding_email_reminders" do + it "works" do + user.onboarding_onboarded_at = Time.now + user.first_onboarding_paid_lesson_at = nil + user.save! + + mailer = mock + UserMailer.deliveries.clear + + LessonSession.onboarding_email_reminders + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_first_free_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + #UserMailer.deliveries.count.should eql 1 + user.reload + user.sent_take_flesson_email_times.should eql 1 + user.sent_take_2nd_flesson_email_times.should eql 0 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + #RSpec::Mocks.space.proxy_for(mailer).reset + #RSpec::Mocks.space.proxy_for(UserMailer).reset + UserMailer.deliveries.clear + + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 1 + user.sent_take_2nd_flesson_email_times.should eql 0 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + #RSpec::Mocks.space.proxy_for(mailer).reset + #RSpec::Mocks.space.proxy_for(UserMailer).reset + UserMailer.deliveries.clear + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_first_free_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 2 + user.sent_take_2nd_flesson_email_times.should eql 0 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + #RSpec::Mocks.space.proxy_for(mailer).reset + #RSpec::Mocks.space.proxy_for(UserMailer).reset + UserMailer.deliveries.clear + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_first_free_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 0 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + #RSpec::Mocks.space.proxy_for(mailer).reset + #RSpec::Mocks.space.proxy_for(UserMailer).reset + UserMailer.deliveries.clear + + Timecop.travel(Date.today + 3) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 0 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_true + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + #RSpec::Mocks.space.proxy_for(mailer).reset + #RSpec::Mocks.space.proxy_for(UserMailer).reset + UserMailer.deliveries.clear + + user.first_onboarding_free_lesson_at = Time.now + user.save! + + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 0 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_second_free_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 1 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_second_free_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 2 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_second_free_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_true + user.stuck_take_plesson.should be_false + + user.second_onboarding_free_lesson_at = Time.now + user.save! + + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 0 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_paid_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 1 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_paid_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 2 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + mailer.should_receive(:deliver_now).once + UserMailer.should_receive(:take_paid_lesson).and_return(mailer) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 3 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_false + + Timecop.travel(Date.today + 3) + LessonSession.onboarding_email_reminders + user.reload + user.sent_take_flesson_email_times.should eql 3 + user.sent_take_2nd_flesson_email_times.should eql 3 + user.sent_take_plesson_email_times.should eql 3 + user.stuck_take_flesson.should be_false + user.stuck_take_2nd_flesson.should be_false + user.stuck_take_plesson.should be_true + + end + end + describe "index" do it "finds single lesson as student" do diff --git a/ruby/spec/mailers/render_emails_spec.rb b/ruby/spec/mailers/render_emails_spec.rb index 7db4da562..2425f5661 100644 --- a/ruby/spec/mailers/render_emails_spec.rb +++ b/ruby/spec/mailers/render_emails_spec.rb @@ -210,6 +210,113 @@ describe "RenderMailers", :slow => true do UserMailer.deliveries.clear UserMailer.student_no_comm_other(lesson.lesson_booking, true).deliver_now end + + it "take_first_free_lesson 1" do + @filename = "take_first_free_lesson_1" + + user.sent_take_flesson_email_times = 0 + user.save! + + UserMailer.deliveries.clear + UserMailer.take_first_free_lesson(user).deliver_now + end + + it "take_first_free_lesson 2" do + @filename = "take_first_free_lesson_2" + + user.sent_take_flesson_email_times = 1 + user.save! + + UserMailer.deliveries.clear + UserMailer.take_first_free_lesson(user).deliver_now + end + + it "take_first_free_lesson 3" do + @filename = "take_first_free_lesson_3" + + user.sent_take_flesson_email_times = 2 + user.save! + + UserMailer.deliveries.clear + UserMailer.take_first_free_lesson(user).deliver_now + end + + it "take_second_free_lesson 1" do + @filename = "take_second_free_lesson_1" + + user.sent_take_2nd_flesson_email_times = 0 + user.save! + + lesson = testdrive_lesson(user, teacher) + + + UserMailer.deliveries.clear + UserMailer.take_second_free_lesson(user).deliver_now + end + + it "take_second_free_lesson 2" do + @filename = "take_second_free_lesson_2" + + user.sent_take_2nd_flesson_email_times = 1 + user.save! + + lesson = testdrive_lesson(user, teacher) + + UserMailer.deliveries.clear + UserMailer.take_second_free_lesson(user).deliver_now + end + + it "take_second_free_lesson 3" do + @filename = "take_second_free_lesson_3" + + user.sent_take_2nd_flesson_email_times = 2 + user.save! + + lesson = testdrive_lesson(user, teacher) + + UserMailer.deliveries.clear + UserMailer.take_second_free_lesson(user).deliver_now + end + + it "take_paid_lesson 1" do + @filename = "take_paid_lesson_1" + + user.sent_take_plesson_email_times = 0 + user.save! + + lesson = testdrive_lesson(user, teacher) + lesson = testdrive_lesson(user, teacher) + + + UserMailer.deliveries.clear + UserMailer.take_paid_lesson(user).deliver_now + end + + it "take_paid_lesson 2" do + @filename = "take_paid_lesson_2" + + user.sent_take_plesson_email_times = 1 + user.save! + + lesson = testdrive_lesson(user, teacher) + lesson = testdrive_lesson(user, teacher) + + UserMailer.deliveries.clear + UserMailer.take_paid_lesson(user).deliver_now + end + + it "take_paid_lesson 3" do + @filename = "take_paid_lesson_3" + + user.sent_take_plesson_email_times = 2 + user.save! + + lesson = testdrive_lesson(user, teacher) + lesson = testdrive_lesson(user, teacher) + + UserMailer.deliveries.clear + UserMailer.take_paid_lesson(user).deliver_now + end end end From 48360fbaec5f0475d55081341bf174913e4e24b7 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 25 Feb 2018 16:49:04 -0600 Subject: [PATCH 05/13] intclude mark lost feature --- admin/app/admin/onboarding.rb | 8 ++++++++ ruby/lib/jam_ruby/models/user.rb | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/admin/app/admin/onboarding.rb b/admin/app/admin/onboarding.rb index 33eb7420e..8e4358691 100644 --- a/admin/app/admin/onboarding.rb +++ b/admin/app/admin/onboarding.rb @@ -96,6 +96,9 @@ ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do if user.is_waiting_onboarding || user.is_onboarding link_to("mark onboarded", mark_onboarded_admin_onboarder_management_path(user.id), { 'data-confirm': "Mark onboarded?"}) end + if user.is_waiting_onboarding || user.is_onboarding + link_to("mark lost", mark_lost_admin_onboarder_management_path(user.id), { 'data-confirm': "Mark lost (reason = Other)?"}) + end end end column "Onboarder Notes", :onboarding_onboarder_notes @@ -127,4 +130,9 @@ ActiveAdmin.register JamRuby::User, :as => 'OnboarderManagement' do redirect_to :back end + member_action :mark_lost, :method => :get do + resource.mark_lost + redirect_to :back + end + end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 535eaae81..4130caa43 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -353,7 +353,7 @@ module JamRuby end def is_onboarding - ONBOARDING_STATUS_ASSIGNED || ONBOARDING_STATUS_ESCALATED || ONBOARDING_STATUS_EMAILED || ONBOARDING_STATUS_LOST + ONBOARDING_STATUS_ASSIGNED || ONBOARDING_STATUS_ESCALATED || ONBOARDING_STATUS_EMAILED end def user_progression_fields @@ -2543,6 +2543,12 @@ module JamRuby self.save! end + def mark_lost(lost_reason = LOST_REASON_OTHER) + self.onboarding_lost_at = Time.now + self.onboarding_lost_reason = lost_reason + self.save! + end + def has_booked_with_student?(student, since_at = nil) LessonBooking.engaged_bookings(student, self, since_at).count > 0 end From afecb2e369e8dbaff7dc87c1d349f65ec71300aa Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 25 Feb 2018 17:55:00 -0600 Subject: [PATCH 06/13] randomize order --- db/up/onboarding_emails.sql | 4 +- ruby/lib/jam_ruby/app/mailers/user_mailer.rb | 41 +++++++++++++++++-- .../user_mailer/onboarding_survey.html.erb | 14 +++++++ .../user_mailer/onboarding_survey.text.erb | 9 ++++ ruby/lib/jam_ruby/models/teacher.rb | 6 ++- .../jam_ruby/resque/scheduled/daily_job.rb | 3 ++ ruby/spec/jam_ruby/models/teacher_spec.rb | 12 +++--- .../react-components/BookLesson.js.jsx.coffee | 2 + .../client/react-components/BookLesson.scss | 1 + .../react-components/LessonBooking.scss | 5 +++ 10 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb create mode 100644 ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.text.erb diff --git a/db/up/onboarding_emails.sql b/db/up/onboarding_emails.sql index e2312537f..c02f9799c 100644 --- a/db/up/onboarding_emails.sql +++ b/db/up/onboarding_emails.sql @@ -11,7 +11,7 @@ ALTER TABLE users ADD COLUMN sent_admin_take_plesson_email_at TIMESTAMP WITHOUT ALTER TABLE users ADD COLUMN stuck_take_flesson BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE users ADD COLUMN stuck_take_2nd_flesson BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE users ADD COLUMN stuck_take_plesson BOOLEAN NOT NULL DEFAULT FALSE; - +ALTER TABLE teachers ADD COLUMN random_order INTEGER NOT NULL DEFAULT 0; CREATE INDEX index_first_onboarding_paid_lesson_at ON users USING btree(first_onboarding_paid_lesson_at); CREATE INDEX index_onboarding_onboarded_at ON users USING btree(onboarding_onboarded_at); CREATE INDEX index_onboarding_status ON users USING btree(onboarding_status); @@ -22,3 +22,5 @@ CREATE INDEX index_sent_admin_take_flesson_email_at ON users USING btree(sent_ad CREATE INDEX index_sent_admin_take_2nd_flesson_email_at ON users USING btree(sent_admin_take_2nd_flesson_email_at); CREATE INDEX index_sent_admin_take_plesson_email_at ON users USING btree(sent_admin_take_plesson_email_at); CREATE INDEX index_posa_cards_lesson_package_type_id ON posa_cards USING btree(lesson_package_type_id); + +UPDATE teachers set random_order = sub.row_number * random() * 1000 from (select id, row_number() over () from teachers) as sub ; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb index b10b6a22e..9cece2313 100644 --- a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb @@ -54,7 +54,13 @@ module JamRuby @lesson = user.taken_lessons.order(:created_at).last @teacher = @lesson.teacher - @subject = "Time to get even better at your instrument" + if user.sent_take_plesson_email_times == 0 + @subject = "Book your next lesson with your instructor now!" + elsif user.sent_take_plesson_email_times == 1 + @subject = "Book your next lesson to continue your musical journey" + else + @subject = "The best way to improve on your instrument is with a great instructor - book today!" + end sendgrid_category "promo_lesson_reminder" sendgrid_unique_args :type => "promo_lesson_reminder" @@ -72,7 +78,14 @@ module JamRuby @lesson = user.taken_lessons.order(:created_at).last @teacher = @lesson.teacher - @subject = "A reminder to take your second free lesson" + if user.sent_take_2nd_flesson_email_times == 0 + @subject = "Book your second free lesson now!" + elsif user.sent_take_2nd_flesson_email_times == 1 + @subject = "Book your second free lesson to continue your musical journey" + else + @subject = "Last reminder to book your next free lesson" + end + sendgrid_category "promo_lesson_reminder" sendgrid_unique_args :type => "promo_lesson_reminder" @@ -87,7 +100,14 @@ module JamRuby def take_first_free_lesson(user) @user = user - @subject = "A reminder to take your first free lesson" + if user.sent_take_flesson_email_times == 0 + @subject = "Book your first free lesson now!" + elsif user.sent_take_flesson_email_times == 1 + @subject = "Book your first free lesson to start your musical journey" + else + @subject = "Last reminder to book your free lessons - $60 value!" + end + sendgrid_category "promo_lesson_reminder" sendgrid_unique_args :type => "promo_lesson_reminder" @@ -100,6 +120,21 @@ module JamRuby end end + def onboarding_survey(user) + @user = user + @subject = "1-minute JamKazam survey - please help us give good support!" + sendgrid_category "onboarding_survey" + sendgrid_unique_args :type => "onboarding_survey" + + sendgrid_recipients([user.email]) + sendgrid_substitute('@USERID', [user.id]) + + mail(:to => user.email, :subject => @subject) do |format| + format.text + format.html + end + end + def student_education_welcome_message(user) @user = user @subject = "Welcome to JamKazam and JamClass online lessons!" diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb new file mode 100644 index 000000000..014275201 --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb @@ -0,0 +1,14 @@ +<% provide(:title, @subject) %> + + +<% if !@user.anonymous? %> +

Hi <%= @user.first_name %>, +

+<% end %> + +

+ Please click this link to take a literally 1-minute survey on your onboarding experience: https://www.surveymonkey.com/r/93JC2KJ. This will help us deliver better support to other students like you. Thank you! +

+ +

Best Regards,
+ Team JamKazam

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.text.erb new file mode 100644 index 000000000..91abd4b12 --- /dev/null +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.text.erb @@ -0,0 +1,9 @@ +<% if !@user.anonymous? %> +Hi <%= @user.first_name %>, +<% end %> + +Please click this link to take a literally 1-minute survey on your onboarding experience: https://www.surveymonkey.com/r/93JC2KJ. This will help us deliver better support to other students like you. Thank you! + + +Best Regards, +Team JamKazam diff --git a/ruby/lib/jam_ruby/models/teacher.rb b/ruby/lib/jam_ruby/models/teacher.rb index 09515a4ba..0da61ccfd 100644 --- a/ruby/lib/jam_ruby/models/teacher.rb +++ b/ruby/lib/jam_ruby/models/teacher.rb @@ -148,7 +148,7 @@ module JamRuby # Real teachers who are marked as top. # Real teachers who are not marked as top. # Phantom teachers. - query = query.order("top_rated DESC, phantom ASC") + query = query.order("phantom ASC, random_order ASC") current_page = params[:page].nil? ? 1 : params[:page].to_i next_page = current_page + 1 @@ -165,6 +165,10 @@ module JamRuby end end + def self.randomize_order + self.connection.execute("update teachers set random_order = sub.row_number * random() * 1000 from (select id, row_number() over () from teachers) as sub ;") + end + def self.save_teacher(user, params) teacher = nil Teacher.transaction do diff --git a/ruby/lib/jam_ruby/resque/scheduled/daily_job.rb b/ruby/lib/jam_ruby/resque/scheduled/daily_job.rb index 286285b4b..6e39d635e 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/daily_job.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/daily_job.rb @@ -8,6 +8,8 @@ module JamRuby def self.perform @@log.debug("waking up") + Teacher.randomize_order + bounced_emails calendar_manager = CalendarManager.new @@ -15,6 +17,7 @@ module JamRuby MusicSession.cleanup_old_sessions + @@log.debug("done") end diff --git a/ruby/spec/jam_ruby/models/teacher_spec.rb b/ruby/spec/jam_ruby/models/teacher_spec.rb index 71c697063..9c7e59878 100644 --- a/ruby/spec/jam_ruby/models/teacher_spec.rb +++ b/ruby/spec/jam_ruby/models/teacher_spec.rb @@ -28,18 +28,18 @@ describe Teacher do teacher2 = FactoryGirl.create(:teacher, ready_for_session_at: Time.now) - teacher2.top_rated = false + teacher2.random_order = 1 teacher2.save! teacher1 = FactoryGirl.create(:teacher, ready_for_session_at: Time.now) - teacher1.top_rated = true + teacher1.random_order = 2 teacher1.save! query = Teacher.index(nil, {})[:query] query.count.should eql 3 - query[0].should eq(teacher1.user) - query[1].should eq(teacher2.user) + query[0].should eq(teacher2.user) + query[1].should eq(teacher1.user) query[2].should eq(teacher3.user) end @@ -228,8 +228,8 @@ describe Teacher do end it "student only sees guitar center teachers" do - teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now) - gc_teacher = FactoryGirl.create(:teacher, school: @gc_school, ready_for_session_at: Time.now) + teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now, random_order: 1) + gc_teacher = FactoryGirl.create(:teacher, school: @gc_school, ready_for_session_at: Time.now, random_order: 2) # TODO: perhaps GC teachers should not come back to non-GC users. Not sure yet. query = Teacher.index(user, {})[:query] diff --git a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee index 1e36f0d09..1318ae21e 100644 --- a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee @@ -380,6 +380,7 @@ UserStore = context.UserStore : +
* Time will be local to {window.JK.currentTimezone()}
{slot1Errors}
@@ -394,6 +395,7 @@ UserStore = context.UserStore :
+
* Time will be local to {window.JK.currentTimezone()}
{slot2Errors} ` diff --git a/web/app/assets/stylesheets/client/react-components/BookLesson.scss b/web/app/assets/stylesheets/client/react-components/BookLesson.scss index 20bad39ab..837f13a0d 100644 --- a/web/app/assets/stylesheets/client/react-components/BookLesson.scss +++ b/web/app/assets/stylesheets/client/react-components/BookLesson.scss @@ -10,4 +10,5 @@ div[data-react-class="BookLesson"] { @include border_box_sizing; } + } diff --git a/web/app/assets/stylesheets/client/react-components/LessonBooking.scss b/web/app/assets/stylesheets/client/react-components/LessonBooking.scss index fbed0c14d..69da51f23 100644 --- a/web/app/assets/stylesheets/client/react-components/LessonBooking.scss +++ b/web/app/assets/stylesheets/client/react-components/LessonBooking.scss @@ -67,6 +67,11 @@ input { display:inline-block; } + + .tz-notice { + margin-left: 2px; + margin-top: 13px; + } textarea { width:100%; @include border_box_sizing; From 715e790bf690be5b1d91689b4add6b23335a415f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 25 Feb 2018 18:01:19 -0600 Subject: [PATCH 07/13] clean up presentation --- .../javascripts/react-components/BookLesson.js.jsx.coffee | 5 +++-- .../stylesheets/client/react-components/LessonBooking.scss | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee index 1318ae21e..75a5a9989 100644 --- a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee @@ -379,8 +379,8 @@ UserStore = context.UserStore : + {window.JK.currentTimezone()} -
* Time will be local to {window.JK.currentTimezone()}
{slot1Errors}
@@ -394,8 +394,9 @@ UserStore = context.UserStore : + {window.JK.currentTimezone()}
-
* Time will be local to {window.JK.currentTimezone()}
+ {slot2Errors} ` diff --git a/web/app/assets/stylesheets/client/react-components/LessonBooking.scss b/web/app/assets/stylesheets/client/react-components/LessonBooking.scss index 69da51f23..02bb8d30a 100644 --- a/web/app/assets/stylesheets/client/react-components/LessonBooking.scss +++ b/web/app/assets/stylesheets/client/react-components/LessonBooking.scss @@ -69,8 +69,7 @@ } .tz-notice { - margin-left: 2px; - margin-top: 13px; + margin-left: 15px; } textarea { width:100%; From 89f69cfdca2d57db8ab1df3ccb8ac774e0cdca9a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 25 Feb 2018 20:54:02 -0600 Subject: [PATCH 08/13] send survey db stuff --- db/up/onboarding_emails.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/db/up/onboarding_emails.sql b/db/up/onboarding_emails.sql index c02f9799c..4e4d3113e 100644 --- a/db/up/onboarding_emails.sql +++ b/db/up/onboarding_emails.sql @@ -12,6 +12,8 @@ ALTER TABLE users ADD COLUMN stuck_take_flesson BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE users ADD COLUMN stuck_take_2nd_flesson BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE users ADD COLUMN stuck_take_plesson BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE teachers ADD COLUMN random_order INTEGER NOT NULL DEFAULT 0; +ALTER TABLE users ADD COLUMN send_onboarding_survey BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE users ADD COLUMN sent_onboarding_survey_at TIMESTAMP WITHOUT TIME ZONE; CREATE INDEX index_first_onboarding_paid_lesson_at ON users USING btree(first_onboarding_paid_lesson_at); CREATE INDEX index_onboarding_onboarded_at ON users USING btree(onboarding_onboarded_at); CREATE INDEX index_onboarding_status ON users USING btree(onboarding_status); From 698e6439960546717d076a42786934112b2ac710 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 25 Feb 2018 21:44:02 -0600 Subject: [PATCH 09/13] onboarding survey --- admin/app/admin/dashboard.rb | 1 + .../user_mailer/onboarding_survey.html.erb | 2 - ruby/lib/jam_ruby/models/user.rb | 16 +++++ .../jam_ruby/resque/scheduled/hourly_job.rb | 2 +- ruby/spec/jam_ruby/models/user_spec.rb | 72 +++++++++++++++++++ ruby/spec/mailers/render_emails_spec.rb | 6 ++ 6 files changed, 96 insertions(+), 3 deletions(-) diff --git a/admin/app/admin/dashboard.rb b/admin/app/admin/dashboard.rb index f4d8820f6..e4c5ff6db 100644 --- a/admin/app/admin/dashboard.rb +++ b/admin/app/admin/dashboard.rb @@ -10,6 +10,7 @@ ActiveAdmin.register_page "Dashboard" do li link_to "Users", admin_users_path li link_to "Teachers", admin_teachers_path li link_to "Onboarding Management", admin_onboarder_managements_path + li link_to "Onboarding Settings", admin_onboarders_path li link_to "Lesson Sessions", admin_lesson_sessions_path li link_to "Slow Lesson Responses", admin_slow_responses_path diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb index 014275201..f552b2d7b 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/onboarding_survey.html.erb @@ -1,5 +1,3 @@ -<% provide(:title, @subject) %> - <% if !@user.anonymous? %>

Hi <%= @user.first_name %>, diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 4130caa43..9944ec233 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -339,9 +339,25 @@ module JamRuby updates[:stuck_take_plesson] = false end + if updates[:onboarding_status] == ONBOARDING_STATUS_ONBOARDED || updates[:onboarding_status] == ONBOARDING_STATUS_LOST || updates[:onboarding_status] == ONBOARDING_STATUS_ESCALATED + self.send_onboarding_survey = true + end + User.where(id: self.id).update_all(updates) end end + + def self.hourly_check + send_onboarding_surveys + end + + def self.send_onboarding_surveys + User.where(send_onboarding_survey: true, sent_onboarding_survey_at: nil).each do |user| + UserMailer.onboarding_survey(user).deliver_now + User.where(id: user.id).update_all(sent_onboarding_survey_at: Time.now) + end + end + def update_teacher_pct if teacher teacher.update_profile_pct diff --git a/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb b/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb index c603a1f51..d2d64000c 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb @@ -11,7 +11,7 @@ module JamRuby LessonBooking.hourly_check LessonSession.hourly_check TeacherPayment.hourly_check - + User.hourly_check @@log.debug("done") end diff --git a/ruby/spec/jam_ruby/models/user_spec.rb b/ruby/spec/jam_ruby/models/user_spec.rb index 3d3bfe62f..e88b5c3ce 100644 --- a/ruby/spec/jam_ruby/models/user_spec.rb +++ b/ruby/spec/jam_ruby/models/user_spec.rb @@ -931,6 +931,78 @@ describe User do end end end + describe "onboarding_status" do + let(:user) {FactoryGirl.create(:user)} + + it "onboarded" do + user.send_onboarding_survey.should be_false + user.onboarding_onboarded_at = Time.now + user.save! + user.send_onboarding_survey.should be_true + user.onboarding_status = User::ONBOARDING_STATUS_ONBOARDED + end + + it "lost" do + user.send_onboarding_survey.should be_false + user.onboarding_lost_reason = User::LOST_REASON_NO_VIDEO_STREAM + user.save! + user.send_onboarding_survey.should be_true + user.onboarding_status = User::ONBOARDING_STATUS_LOST + end + + it "escalated" do + user.send_onboarding_survey.should be_false + user.onboarding_escalation_reason = User::ESCALATION_REASON_NO_VIDEO_STREAM + user.save! + user.send_onboarding_survey.should be_true + user.onboarding_status = User::ONBOARDING_STATUS_ESCALATED + end + + it "taken free lesson" do + user.send_onboarding_survey.should be_false + user.first_onboarding_free_lesson_at = Time.now + user.save! + user.send_onboarding_survey.should be_false + user.onboarding_status = User::ONBOARDING_STATUS_FREE_LESSON + end + + it "paid lesson" do + user.send_onboarding_survey.should be_false + user.first_onboarding_paid_lesson_at = Time.now + user.save! + user.send_onboarding_survey.should be_false + user.onboarding_status = User::ONBOARDING_STATUS_PAID_LESSON + end + + it "assigned" do + user.send_onboarding_survey.should be_false + user.onboarder = FactoryGirl.create(:user, is_onboarder:true) + user.save! + user.send_onboarding_survey.should be_false + user.onboarding_status = User::ONBOARDING_STATUS_ASSIGNED + end + + end + describe "send_onboarding_surveys" do + let(:user) {FactoryGirl.create(:user)} + + it "works" do + UserMailer.deliveries.clear + user.send_onboarding_survey = true + user.save! + + User.send_onboarding_surveys + + UserMailer.deliveries.count.should eql 1 + user.reload + user.send_onboarding_survey.should be_true + user.sent_onboarding_survey_at.should_not be_nil + + UserMailer.deliveries.clear + User.send_onboarding_surveys + UserMailer.deliveries.count.should eql 0 + end + end =begin describe "update avatar" do diff --git a/ruby/spec/mailers/render_emails_spec.rb b/ruby/spec/mailers/render_emails_spec.rb index 2425f5661..9ec2f6783 100644 --- a/ruby/spec/mailers/render_emails_spec.rb +++ b/ruby/spec/mailers/render_emails_spec.rb @@ -317,6 +317,12 @@ describe "RenderMailers", :slow => true do UserMailer.deliveries.clear UserMailer.take_paid_lesson(user).deliver_now end + + it "onboarding_survey" do + @filename = "onboarding_survey" + UserMailer.deliveries.clear + UserMailer.onboarding_survey(user).deliver_now + end end end From 8cb9db997190f3dd849815b7627e55bb8e20ec8f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 3 Mar 2018 15:39:55 -0600 Subject: [PATCH 10/13] show non-WASAPI in chat. fix survey --- admin/app/views/admin/users/_form.html.slim | 1 + db/manifest | 3 ++- db/up/limit_counter_reminders.sql | 1 + ruby/lib/jam_ruby/models/lesson_booking.rb | 7 ++++- ruby/lib/jam_ruby/models/lesson_session.rb | 2 +- ruby/lib/jam_ruby/models/user.rb | 2 +- .../jam_ruby/models/lesson_session_spec.rb | 17 ++++++++++++ web/app/assets/javascripts/ftue.js | 2 +- .../AccountOnboarderScreen.js.jsx.coffee | 6 +++++ .../assets/javascripts/wizard/gear_utils.js | 26 ++++++++++++++++++- 10 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 db/up/limit_counter_reminders.sql diff --git a/admin/app/views/admin/users/_form.html.slim b/admin/app/views/admin/users/_form.html.slim index bb666c081..df29c3115 100644 --- a/admin/app/views/admin/users/_form.html.slim +++ b/admin/app/views/admin/users/_form.html.slim @@ -4,6 +4,7 @@ = f.input :admin = f.input :is_onboarder, label: 'Is Support Consultant' = f.input :subscribe_email, label: 'Subscribed to Emails?' + = f.input :gifted_jamtracks, label: 'JamTrack Credits' = f.input :first_name = f.input :last_name = f.input :city diff --git a/db/manifest b/db/manifest index ece0a3d84..b8a025a75 100755 --- a/db/manifest +++ b/db/manifest @@ -386,4 +386,5 @@ better_lesson_notices.sql teacher_search_control.sql user_timezone.sql onboarder_limit.sql -onboarding_emails.sql \ No newline at end of file +onboarding_emails.sql +limit_counter_reminders.sql \ No newline at end of file diff --git a/db/up/limit_counter_reminders.sql b/db/up/limit_counter_reminders.sql new file mode 100644 index 000000000..08d57e0a8 --- /dev/null +++ b/db/up/limit_counter_reminders.sql @@ -0,0 +1 @@ +ALTER TABLE lesson_sessions ADD COLUMN counter_reminders INTEGER NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb index 758c94710..39e2fdfd1 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking.rb @@ -737,7 +737,12 @@ module JamRuby self.autocanceling = true self.active = false self.status = STATUS_UNCONFIRMED - save + if save + if is_test_drive? + user.jamclass_credits = user.jamclass_credits + 1 + user.save(validate:false) + end + end self end diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb index 83a2f116c..fe9a00f4f 100644 --- a/ruby/lib/jam_ruby/models/lesson_session.rb +++ b/ruby/lib/jam_ruby/models/lesson_session.rb @@ -147,7 +147,7 @@ module JamRuby if lesson_session.student_last_proposed? relevant_time = [lesson_session.countered_at, lesson_session.lesson_booking.sent_notices_at].find { |x| !x.nil? } - UserMailer.student_no_comm_other(lesson_session.lesson_booking, relevant_time < Time.now - 3.days).deliver_now + UserMailer.student_no_comm_other(lesson_session.lesson_booking, relevant_time < 3.days.ago).deliver_now UserMailer.teacher_counter_reminder(lesson_session).deliver_now else UserMailer.teacher_no_comm_other(lesson_session.lesson_booking).deliver_now diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 9944ec233..76971c1c4 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -340,7 +340,7 @@ module JamRuby end if updates[:onboarding_status] == ONBOARDING_STATUS_ONBOARDED || updates[:onboarding_status] == ONBOARDING_STATUS_LOST || updates[:onboarding_status] == ONBOARDING_STATUS_ESCALATED - self.send_onboarding_survey = true + updates[:send_onboarding_survey] = true end User.where(id: self.id).update_all(updates) diff --git a/ruby/spec/jam_ruby/models/lesson_session_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_spec.rb index c7d0bbfbf..8ab0a4e6f 100644 --- a/ruby/spec/jam_ruby/models/lesson_session_spec.rb +++ b/ruby/spec/jam_ruby/models/lesson_session_spec.rb @@ -9,6 +9,8 @@ describe LessonSession do let(:lesson_booking) { b = LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60); b.card_presumed_ok = true; b.save!; b } let(:lesson_session) { lesson_booking.lesson_sessions[0] } + let(:td_lesson_booking) { b = LessonBooking.book_test_drive(user, teacher, [slot1, slot2], "Hey I've heard of you before."); b.card_presumed_ok = true; b.save!; b } + let(:td_lesson_session) { td_lesson_booking.lesson_sessions[0] } describe "counter" do describe "recurring" do @@ -215,6 +217,21 @@ describe LessonSession do end describe "autocancel" do + it "returns credit" do + + jamclass_credits = user.jamclass_credits + + td_lesson_session.status.should eql LessonSession::STATUS_REQUESTED + + Timecop.travel(Date.today + 10) + + td_lesson_session.autocancel + td_lesson_session.reload + td_lesson_session.status.should eql LessonSession::STATUS_UNCONFIRMED + td_lesson_session.lesson_booking.status.should eql LessonSession::STATUS_UNCONFIRMED + user.reload + user.jamclass_credits.should eql (jamclass_credits + 1) + end it "can't autocancel in the past" do lesson_session.status.should eql LessonSession::STATUS_REQUESTED diff --git a/web/app/assets/javascripts/ftue.js b/web/app/assets/javascripts/ftue.js index ec8d37e4e..54ee2c1a0 100644 --- a/web/app/assets/javascripts/ftue.js +++ b/web/app/assets/javascripts/ftue.js @@ -495,7 +495,7 @@ */ function loadAudioDrivers() { var drivers = context.jamClient.FTUEGetDevices(false); - var chatDrivers = jamClient.FTUEGetChatInputs(); + var chatDrivers = jamClient.FTUEGetChatInputs(false, false); var optionsHtml = ''; var chatOptionsHtml = ''; diff --git a/web/app/assets/javascripts/react-components/AccountOnboarderScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/AccountOnboarderScreen.js.jsx.coffee index d9a45305a..ca1cc2083 100644 --- a/web/app/assets/javascripts/react-components/AccountOnboarderScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/AccountOnboarderScreen.js.jsx.coffee @@ -149,8 +149,14 @@ profileUtils = context.JK.ProfileUtils value = $target.val() options = {id: id} options[field] = value + @postUpdate(field, value) @queueUpdate(options) + postUpdate: (field, value) -> + if field == 'onboarding_onboarded_at' + console.log("onboarding onboarded at set") + context.JK.Banner.showAlert({title: "One Last Thing", html: "Please send this student Email #5 right now.

This is a critical email, and if you don't send it now, you'll forget. Thanks!"}); + queueUpdate: (update) -> @updates.push(update) @onUpdate() diff --git a/web/app/assets/javascripts/wizard/gear_utils.js b/web/app/assets/javascripts/wizard/gear_utils.js index 6f438dfd8..a1002a24e 100644 --- a/web/app/assets/javascripts/wizard/gear_utils.js +++ b/web/app/assets/javascripts/wizard/gear_utils.js @@ -354,7 +354,31 @@ var musicPorts = jamClient.FTUEGetChannels(); //var chatsOnCurrentDevice = context.jamClient.FTUEGetChatInputs(true); - var chatsOnOtherDevices = context.jamClient.FTUEGetChatInputs(false); + var chatsOnOtherDevices = context.jamClient.FTUEGetChatInputs(false, false); + + // remove all virtual/remote junk form chat inputs. Their key contains' JamKazam when it's Virtual Input or Remote + Object.keys(chatsOnOtherDevices).forEach(function(key) { + if(key.indexOf('JamKazam') > -1) { + delete chatsOnOtherDevices[key] + } + } ) + + // this wasapi logic is this: if there are any non-WASAPI items, present only them, because WASAPI is very high latency and + // highly desirable. But if the user ONLY has wasapi, then fine, we show them (by not deleting them from the list) + var allWasapi = true; + context._.each(chatsOnOtherDevices, function (chatChannelName, chatChannelId) { + if(chatChannelName.indexOf('WASAPI') == -1) { + allWasapi = false; + return false + } + }) + if(!allWasapi) { + Object.keys(chatsOnOtherDevices).forEach(function(key) { + if(chatsOnOtherDevices[key].indexOf('WASAPI') > -1) { + delete chatsOnOtherDevices[key] + } + } ) + } var chatInputs = []; //context._.each(musicPorts.inputs, function(input) { From f00814fad8f7bca303960b4bb6b512e523e6df00 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 4 Mar 2018 09:30:52 -0600 Subject: [PATCH 11/13] jamtracks gifted fix in admin --- admin/app/admin/jam_ruby_users.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/app/admin/jam_ruby_users.rb b/admin/app/admin/jam_ruby_users.rb index c079792e9..60f1dda4f 100644 --- a/admin/app/admin/jam_ruby_users.rb +++ b/admin/app/admin/jam_ruby_users.rb @@ -261,6 +261,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do @user.last_name = params[:jam_ruby_user][:last_name] @user.state = params[:jam_ruby_user][:state] @user.city = params[:jam_ruby_user][:city] + @user.gifted_jamtracks = params[:jam_ruby_user][:gifted_jamtracks] if params[:jam_ruby_user][:show_frame_options].to_i == 1 From 3c071f1cc37b4d8bbd53a6015178d9487038b23f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 4 Mar 2018 18:03:56 -0600 Subject: [PATCH 12/13] don't whack wasapi --- web/app/assets/javascripts/wizard/gear_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/assets/javascripts/wizard/gear_utils.js b/web/app/assets/javascripts/wizard/gear_utils.js index a1002a24e..38d0bf13c 100644 --- a/web/app/assets/javascripts/wizard/gear_utils.js +++ b/web/app/assets/javascripts/wizard/gear_utils.js @@ -375,7 +375,7 @@ if(!allWasapi) { Object.keys(chatsOnOtherDevices).forEach(function(key) { if(chatsOnOtherDevices[key].indexOf('WASAPI') > -1) { - delete chatsOnOtherDevices[key] + // delete chatsOnOtherDevices[key] } } ) } From f00949b414c7d85be6f83102eaa1cb56e5d10393 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 4 Mar 2018 18:12:18 -0600 Subject: [PATCH 13/13] only show WASAPI --- web/app/assets/javascripts/wizard/gear_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/assets/javascripts/wizard/gear_utils.js b/web/app/assets/javascripts/wizard/gear_utils.js index 38d0bf13c..7b623f898 100644 --- a/web/app/assets/javascripts/wizard/gear_utils.js +++ b/web/app/assets/javascripts/wizard/gear_utils.js @@ -354,7 +354,7 @@ var musicPorts = jamClient.FTUEGetChannels(); //var chatsOnCurrentDevice = context.jamClient.FTUEGetChatInputs(true); - var chatsOnOtherDevices = context.jamClient.FTUEGetChatInputs(false, false); + var chatsOnOtherDevices = context.jamClient.FTUEGetChatInputs(false, true); // remove all virtual/remote junk form chat inputs. Their key contains' JamKazam when it's Virtual Input or Remote Object.keys(chatsOnOtherDevices).forEach(function(key) {