VRFS-3936 merging develop

This commit is contained in:
Jonathan Kolyer 2018-02-21 19:22:32 -08:00
commit fa3ba71b22
66 changed files with 1370 additions and 109 deletions

View File

@ -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"

View File

@ -95,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)
@ -628,6 +631,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

View File

@ -11,7 +11,8 @@ 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:

View File

@ -9,7 +9,8 @@ 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 "Onboarding Settings", admin_onboarders_path
li link_to "Lesson Sessions", admin_lesson_sessions_path
li link_to "Slow Lesson Responses", admin_slow_responses_path

View File

@ -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,11 +255,13 @@ 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]
@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

View File

@ -0,0 +1,91 @@
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

View File

@ -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?
@ -41,7 +41,11 @@ ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' 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
@ -59,10 +63,23 @@ 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
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,17 @@ ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' 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
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
end
member_action :update_onboarder, :method => :post do
@ -82,4 +110,29 @@ ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' 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
member_action :mark_lost, :method => :get do
resource.mark_lost
redirect_to :back
end
end

View File

@ -12,3 +12,8 @@
// //= require autocomplete-rails
//= require base
//= require_tree .
$(document).ready(function() {
jQuery(".best_in_place").best_in_place()
})

View File

@ -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()

View File

@ -1,5 +1,4 @@
module ApplicationHelper
end

View File

@ -1,2 +1,3 @@
module JamSessionsHelper
end

View File

@ -3,6 +3,8 @@
= 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 :gifted_jamtracks, label: 'JamTrack Credits'
= f.input :first_name
= f.input :last_name
= f.input :city

View File

@ -384,3 +384,7 @@ alter_crash_dumps.sql
onboarding.sql
better_lesson_notices.sql
teacher_search_control.sql
user_timezone.sql
onboarder_limit.sql
onboarding_emails.sql
limit_counter_reminders.sql

View File

@ -0,0 +1 @@
ALTER TABLE lesson_sessions ADD COLUMN counter_reminders INTEGER NOT NULL DEFAULT 0;

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN max_onboardings INTEGER NOT NULL DEFAULT 0;

View File

@ -0,0 +1,28 @@
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;
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);
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);
UPDATE teachers set random_order = sub.row_number * random() * 1000 from (select id, row_number() over () from teachers) as sub ;

2
db/up/user_timezone.sql Normal file
View File

@ -0,0 +1,2 @@
ALTER TABLE users ADD COLUMN timezone VARCHAR;
ALTER TABLE users ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE;

View File

@ -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]

View File

@ -49,6 +49,92 @@ module JamRuby
end
end
def take_paid_lesson(user)
@user = user
@lesson = user.taken_lessons.order(:created_at).last
@teacher = @lesson.teacher
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"
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
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"
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
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"
sendgrid_recipients([user.email])
sendgrid_substitute('@USERID', [user.id])
mail(:to => user.email, :subject => @subject) do |format|
format.text
format.html
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!"

View File

@ -0,0 +1,12 @@
<% if !@user.anonymous? %>
<p>Hi <%= @user.first_name %>,
</p>
<% end %>
<p>
Please click this link to take a literally 1-minute survey on your onboarding experience: <a href="https://www.surveymonkey.com/r/93JC2KJ" style="color:#fc0">https://www.surveymonkey.com/r/93JC2KJ</a>. This will help us deliver better support to other students like you. Thank you!
</p>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -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

View File

@ -0,0 +1,45 @@
<% provide(:title, @subject) %>
<% if !@user.anonymous? %>
<p>Hi <%= @user.first_name %>,
</p>
<% end %>
<% if @user.sent_take_flesson_email_times == 0 %>
<p>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!
</p>
<p>
Here is an article that explains how to search for your ideal teacher: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. And here is an article that explains how to book your first lesson: <a href="http://bit.ly/2k3qNMT" style="color:#fc0">http://bit.ly/2k3qNMT</a>.
</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% elsif @user.sent_take_flesson_email_times == 1 %>
<p>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.</p>
<p>
Here is an article that explains how to search for your ideal teacher: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. And here is an article that explains how to book your first lesson: <a href="http://bit.ly/2k3qNMT" style="color:#fc0">http://bit.ly/2k3qNMT</a>.
</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% else %>
<p>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!</p>
<p>
Here is an article that explains how to search for your ideal teacher: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. And here is an article that explains how to book your first lesson: <a href="http://bit.ly/2k3qNMT" style="color:#fc0">http://bit.ly/2k3qNMT</a>.
</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% end %>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -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

View File

@ -0,0 +1,45 @@
<% provide(:title, @subject) %>
<% if !@user.anonymous? %>
<p>Hi <%= @user.first_name %>,
</p>
<% end %>
<% if @user.sent_take_plesson_email_times == 0 %>
<p>
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.
</p>
<p>
You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <a href="<%= @teacher.teacher_profile_url %>" style="color:#fc0"><%= @teacher.teacher_profile_url %></a>.
</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% elsif @user.sent_take_plesson_email_times == 1 %>
<p>
You're off to a great start with your music teacher. Don't lose momentum!
</p>
<p>
You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <a href="<%= @teacher.teacher_profile_url %>" style="color:#fc0"><%= @teacher.teacher_profile_url %></a>.
</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% else %>
<p>
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.
</p>
<p>
You can book your regular weekly or one-at-a-time lessons with your instructor at his/her profile page here: <a href="<%= @teacher.teacher_profile_url %>" style="color:#fc0"><%= @teacher.teacher_profile_url %></a>.
</p>
<p>
Or if you need to find a different instructor for any reason, here's an article on how to search for your ideal teacher: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. And here is an article that explains how to book your lesson: <a href="http://bit.ly/2k3qNMT" style="color:#fc0">http://bit.ly/2k3qNMT</a>.
</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% end %>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -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: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. 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

View File

@ -0,0 +1,29 @@
<% provide(:title, @subject) %>
<% if !@user.anonymous? %>
<p>Hi <%= @user.first_name %>,
</p>
<% end %>
<% if @user.sent_take_2nd_flesson_email_times == 0 %>
<p>We hope you had a great first music lesson with <%= @teacher.name %>. Schedule your second free lesson with this instructor now at this page: <a href="<%= @teacher.teacher_profile_url %>" style="color:#fc0"><%= @teacher.teacher_profile_url %></a>, and continue your musical journey!</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% elsif @user.sent_take_2nd_flesson_email_times == 1 %>
<p>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: <a href="<%= @teacher.teacher_profile_url %>" style="color:#fc0"><%= @teacher.teacher_profile_url %></a>.</p>
<p>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: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. And here is an article that explains how to book your lesson: <a href="http://bit.ly/2k3qNMT" style="color:#fc0">http://bit.ly/2k3qNMT</a>.</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% else %>
<p>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: <a href="<%= @teacher.teacher_profile_url %>" style="color:#fc0"><%= @teacher.teacher_profile_url %></a>.</p>
<p>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: <a href="http://bit.ly/2kBPXBz" style="color:#fc0">http://bit.ly/2kBPXBz</a>. And here is an article that explains how to book your lesson: <a href="http://bit.ly/2k3qNMT" style="color:#fc0">http://bit.ly/2k3qNMT</a>.</p>
<p>
If you have any trouble, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. Or you can call us at <a href="tel:+18773768742">877-376-8742</a>.
</p>
<% end %>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -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

View File

@ -41,6 +41,10 @@ module JamRuby
0
end
def timezone
@cookies[:'browser.timezone']
end
def free_jamtracks
if has_redeemable_jamtrack
1

View File

@ -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
@ -840,7 +845,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
@ -962,6 +968,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

View File

@ -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

View File

@ -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,9 +145,9 @@ 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.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
@ -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)
@ -984,6 +1032,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
@ -1135,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}"
@ -1176,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)

View File

@ -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

View File

@ -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

View File

@ -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,16 +328,50 @@ 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
if updates[:onboarding_status] == ONBOARDING_STATUS_ONBOARDED || updates[:onboarding_status] == ONBOARDING_STATUS_LOST || updates[:onboarding_status] == ONBOARDING_STATUS_ESCALATED
updates[: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
end
end
def is_waiting_onboarding
ONBOARDING_STATUS_UNASSIGNED
end
def is_onboarding
ONBOARDING_STATUS_ASSIGNED || ONBOARDING_STATUS_ESCALATED || ONBOARDING_STATUS_EMAILED
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
@ -350,6 +384,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 +1257,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 +1587,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 +1710,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
@ -2479,6 +2536,35 @@ 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 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
@ -2494,7 +2580,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

View File

@ -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

View File

@ -11,7 +11,7 @@ module JamRuby
LessonBooking.hourly_check
LessonSession.hourly_check
TeacherPayment.hourly_check
User.hourly_check
@@log.debug("done")
end

View File

@ -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
@ -271,19 +288,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 +476,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
@ -517,6 +521,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

View File

@ -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]

View File

@ -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

View File

@ -210,6 +210,119 @@ 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
it "onboarding_survey" do
@filename = "onboarding_survey"
UserMailer.deliveries.clear
UserMailer.onboarding_survey(user).deliver_now
end
end
end

View File

@ -6,6 +6,7 @@
//= require jquery.monkeypatch
//= require jquery_ujs
//= require jquery.cookie
//= require jstz
//= require AAC_underscore
//= require AAA_Log
//= require globals

View File

@ -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();

View File

@ -495,7 +495,7 @@
*/
function loadAudioDrivers() {
var drivers = context.jamClient.FTUEGetDevices(false);
var chatDrivers = jamClient.FTUEGetChatInputs();
var chatDrivers = jamClient.FTUEGetChatInputs(false, false);
var optionsHtml = '<option selected="selected" value="">Choose...</option>';
var chatOptionsHtml = '<option selected="selected" value="">Choose...</option>';

View File

@ -25,6 +25,7 @@
//= require jquery.manageVsts
//= require jquery.lessonSessionActions
//= require jquery.jamblasterOptions
//= require jstz
//= require ResizeSensor
//= require AAA_Log
//= require AAC_underscore

View File

@ -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.<br/><br/>This is a critical email, and if you don't send it now, you'll forget. Thanks!"});
queueUpdate: (update) ->
@updates.push(update)
@onUpdate()

View File

@ -379,6 +379,7 @@ UserStore = context.UserStore
<label>Time:</label>
<select className="hour">{hours}</select> : <select className="minute">{minutes}</select> <select
className="am_pm">{am_pm}</select>
<span className="tz-notice">{window.JK.currentTimezone()}</span>
</div>
{slot1Errors}
</div>
@ -393,7 +394,9 @@ UserStore = context.UserStore
<label>Time:</label>
<select className="hour">{hours}</select> : <select className="minute">{minutes}</select> <select
className="am_pm">{am_pm}</select>
<span className="tz-notice">{window.JK.currentTimezone()}</span>
</div>
{slot2Errors}
</div>
</div>`

View File

@ -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 `<div className="content-body-scroller">
<Nav/>
@ -814,6 +823,10 @@ UserStore = context.UserStore
content = @renderStudentCanceled()
else if @isUnconfirmed()
header = 'this lesson has been automatically canceled'
content = @renderStudentUnconfirmed()
else if @isSuspended()
header = 'this lesson has been suspended'
@ -961,6 +974,11 @@ UserStore = context.UserStore
# <p className="description">Message from {this.other().first_name}:</p>
# <div className="message"></div>
renderStudentUnconfirmed: () ->
@renderUnconfirmed()
renderStudentCountered: () ->
@renderCountered()
@ -1049,9 +1067,15 @@ UserStore = context.UserStore
renderTeacherSuspended: () ->
renderTeacherUnconfirmed: () ->
@renderUnconfirmed()
renderTeacherCountered: () ->
@renderCountered()
renderUnconfirmed: () ->
action = `<p>The JamKazam system has canceled this request because it was never acknowledged after too long of a time.</p>`
renderCanceled: () ->
canceler = @canceler()
myself = @myself()
@ -1071,6 +1095,7 @@ UserStore = context.UserStore
else
action = `<p>You declined this lesson request.</p>`
if @studentViewing()
if @adminCanceled() || @studentCanceled()

View File

@ -30,6 +30,7 @@
//= require jquery.scrollTo
//= require jquery.pulse
//= require howler.core.js
//= require jstz
//= require ResizeSensor
//= require AAA_Log
//= require AAC_underscore

View File

@ -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, 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) {
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) {

View File

@ -10,4 +10,5 @@ div[data-react-class="BookLesson"] {
@include border_box_sizing;
}
}

View File

@ -67,6 +67,10 @@
input {
display:inline-block;
}
.tz-notice {
margin-left: 15px;
}
textarea {
width:100%;
@include border_box_sizing;

View File

@ -35,6 +35,34 @@
transition: .4s;
}
.slider .off-label {
color:white;
position: absolute;
right: 4px;
top: 4px;
font-weight: bold;
display:inline;
-webkit-transition: .4s;
transition: .4s;
}
.slider .on-label{
position: absolute;
left: 3px;
top: 4px;
display:none;
-webkit-transition: .4s;
transition: .4s;
color:white;
font-weight: bold;
}
input:checked + .slider .on-label {
display:inline;
}
input:checked + .slider .off-label {
display:none;
}
input:checked + .slider {
background-color: $ColorScreenPrimary;
}
@ -49,6 +77,12 @@ input:checked + .slider:before {
transform: translateX(26px);
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;

View File

@ -40,7 +40,8 @@ class ApiRecurlyController < ApiController
location: {:country => billing_info[:country], :state => billing_info[:state], :city => billing_info[:city]},
reuse_card: reuse_card_next_time,
affiliate_referral_id: cookies[:affiliate_visitor],
origin: origin_cookie
origin: origin_cookie,
timezone: current_timezone
}
options = User.musician_defaults(request.remote_ip, ApplicationHelper.base_uri(request) + "/confirm", any_user, options)

View File

@ -124,7 +124,8 @@ class ApiUsersController < ApiController
education_interest: params[:education_interest],
affiliate_referral_id: cookies[:affiliate_visitor],
origin: origin_cookie,
test_drive_package: params[:test_drive_package]
test_drive_package: params[:test_drive_package],
timezone: current_timezone
}
options = User.musician_defaults(request.remote_ip, ApplicationHelper.base_uri(request) + "/confirm", any_user, options)

View File

@ -567,6 +567,7 @@ class LandingsController < ApplicationController
render 'amazon_lessons_promo_2', layout: 'basic'
else
@current_user.update_timezone(current_timezone)
body = "Name: #{@current_user.name}\n"
body << "Email: #{@current_user.email}\n"
body << "Admin: #{@current_user.admin_onboarding_url}\n"
@ -588,7 +589,6 @@ class LandingsController < ApplicationController
@terms = terms_of_service ? "checked" : nil
@under13 = under_13 ? "checked" : nil
# try to signup now
@user = UserManager.new.signup(remote_ip: request.remote_ip,
email: params[:email],
@ -599,6 +599,7 @@ class LandingsController < ApplicationController
under_13: under_13,
skip_recaptcha: true,
musician: true,
timezone: current_timezone,
instruments: [{:instrument_id => 'other', :proficiency_level => 1, :priority => 1}])
if @user.errors.any?
first = @user.errors.first

View File

@ -105,6 +105,7 @@ class SessionsController < ApplicationController
first_name: auth_hash[:info][:first_name],
last_name: auth_hash[:info][:last_name],
email: auth_hash[:info][:email],
timezone: current_timezone,
affiliate_referral_id: cookies[:affiliate_visitor])
# Users who sign up using oauth are presumed to have valid email adddresses.
@ -180,7 +181,8 @@ class SessionsController < ApplicationController
terms_of_service: true,
location: {:country => nil, :state => nil, :city => nil},
affiliate_referral_id: cookies[:affiliate_visitor],
origin: origin_cookie
origin: origin_cookie,
timezone: current_timezone
}
options = User.musician_defaults(request.remote_ip, ApplicationHelper.base_uri(request) + "/confirm", any_user, options)

View File

@ -172,6 +172,7 @@ class UsersController < ApplicationController
signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm",
affiliate_referral_id: cookies[:affiliate_visitor],
affiliate_partner: @affiliate_partner,
timezone: current_timezone,
origin: origin_cookie)
# check for errors

View File

@ -71,11 +71,11 @@ module MusicSessionHelper
end
def pretty_scheduled_start(music_session, with_timezone, shorter = false)
music_session.pretty_scheduled_start(with_timezone, shorter)
music_session.pretty_scheduled_start(with_timezone, shorter, current_timezone)
end
def pretty_scheduled_start_slot(slot, with_timezone)
slot.pretty_scheduled_start(with_timezone)
slot.pretty_scheduled_start(with_timezone, current_timezone)
end
def timezone_options

View File

@ -69,6 +69,10 @@ module SessionsHelper
@current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
def current_timezone
@current_timezone ||= cookies[:'browser.timezone'] || any_user.timezone
end
def anonymous_user=(anonymous_user)
@anonymous_user = anonymous_user
end

View File

@ -3,22 +3,48 @@ object @lesson_booking
attributes :id, :status, :lesson_type, :payment_style, :recurring, :teacher_id, :description, :lesson_length, :created_at, :user_id, :active, :accepter_id, :canceler_id, :cancel_message, :booked_price, :card_presumed_ok, :no_slots, :posa_card_id, :posa_card_purchased, :remaining_roll_forward_amount_in_cents, :canceled_by_admin
child(:lesson_booking_slots => :slots) {
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :message, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package
node :pretty_start_time do |slot|
slot.pretty_start_time(true, current_timezone)
end
node :pretty_scheduled_start do |slot|
slot.pretty_scheduled_start(true, current_timezone)
end
}
child(:default_slot => :default_slot) {
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :message, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package
node :pretty_start_time do |slot|
slot.pretty_start_time(true, current_timezone)
end
node :pretty_scheduled_start do |slot|
slot.pretty_scheduled_start(true, current_timezone)
end
}
child(:alt_slot => :alt_slot) {
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :message, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package
node :pretty_start_time do |slot|
slot.pretty_start_time(true, current_timezone)
end
node :pretty_scheduled_start do |slot|
slot.pretty_scheduled_start(true, current_timezone)
end
}
child(:counter_slot => :counter_slot) {
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package, :is_recurring?
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :message, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package, :is_recurring?
node :pretty_scheduled_start_with_timezone do |slot|
pretty_scheduled_start_slot(slot, true)
end
node :pretty_start_time do |slot|
slot.pretty_start_time(true, current_timezone)
end
node :pretty_scheduled_start do |slot|
slot.pretty_scheduled_start(true, current_timezone)
end
}

View File

@ -20,10 +20,16 @@ child(:lesson_booking => :lesson_booking) {
child(:counter_slot => :counter_slot) {
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package, :is_recurring?
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :message, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package, :is_recurring?
node :pretty_scheduled_start_with_timezone do |slot|
pretty_scheduled_start_slot(slot, true)
end
node :pretty_start_time do |slot|
slot.pretty_start_time(true, current_timezone)
end
node :pretty_scheduled_start do |slot|
slot.pretty_scheduled_start(true, current_timezone)
end
}

View File

@ -1,7 +1,7 @@
object @user
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count,
:recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level, :reuse_card, :email_needs_verification, :is_a_teacher, :is_a_student, :is_onboarder
:recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level, :reuse_card, :email_needs_verification, :is_a_teacher, :is_a_student, :is_onboarder, :timezone
node :location do |user|
if user.musician?

View File

@ -73,7 +73,10 @@
<span class="list-search-label">Let new students find me by search:</span>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
<span class="slider">
<span class="on-label">YES</span>
<span class="off-label">NO</span>
</span>
</label>
</div>
<br clear="all" />

View File

@ -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

View File

@ -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

View File

@ -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'