3 versions of test drive available now

This commit is contained in:
Seth Call 2016-05-04 21:20:38 -05:00
parent 0d8341c2d8
commit e46a4b01cd
40 changed files with 882 additions and 225 deletions

View File

@ -343,4 +343,5 @@ teacher_progression.sql
teacher_complete.sql
lessons.sql
lessons_unread_messages.sql
track_school_signups.sql
track_school_signups.sql
add_test_drive_types.sql

View File

@ -0,0 +1,4 @@
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('test-drive-2', 'Test Drive (2)', 'Two reduced-price lessons which you can use to find that ideal teacher.', 'test-drive-2', 29.99);
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('test-drive-1', 'Test Drive (1)', 'One reduced-price lessons which you can use to find that ideal teacher.', 'test-drive-1', 15.99);
UPDATE lesson_package_types set name = 'Test Drive (4)', package_type = 'test-drive-4' WHERE id = 'test-drive';
ALTER TABLE users ADD COLUMN lesson_package_type_id VARCHAR(64) REFERENCES lesson_package_types(id);

View File

@ -18,7 +18,7 @@ TestDrive lets you take 4 full lessons (30 minutes each) from 4 different teache
teacher for you. Finding the right teacher is the single most important determinant of success in your lessons. Would
you marry the first person you ever dated? No? Same here. Pick 4 teachers who look great, and then see who you click
with. It's a phenomenal value, and then you can stick with the best teacher for you.
Click this link to sign up now for TestDrive (https://www.jamkazam.com/client#/jamclass/book-lesson/purchase_test-drive).
Click this link to sign up now for TestDrive (https://www.jamkazam.com/client#/jamclass/test-drive-selection).
Then you can book 4 TestDrive lessons to get rolling.
2. Set Up Your Gear

View File

@ -410,11 +410,26 @@ module JamRuby
self.save
end
def resolved_test_drive_package
result = nil
purchase = student.most_recent_test_drive_purchase
if purchase
# for lessons already packaged
result = purchase.lesson_package_type
else
# for unbooked lessons
result = student.desired_package
end
if result.nil?
result = LessonPackageType.test_drive_4
end
result
end
def lesson_package_type
if is_single_free?
LessonPackageType.single_free
elsif is_test_drive?
LessonPackageType.test_drive
resolved_test_drive_package
elsif is_normal?
LessonPackageType.single
end
@ -451,7 +466,7 @@ module JamRuby
if is_single_free?
0
elsif is_test_drive?
LessonPackageType.test_drive.price
resolved_test_drive_package.price
elsif is_normal?
teacher.teacher.booking_price(lesson_length, payment_style != PAYMENT_STYLE_MONTHLY)
end

View File

@ -26,7 +26,7 @@ module JamRuby
def validate_test_drive
if user
if !user.can_buy_test_drive?
if lesson_package_type.is_test_drive? && !user.can_buy_test_drive?
errors.add(:user, "can not buy test drive right now because you have already purchased it within the last year")
end
end
@ -42,7 +42,7 @@ module JamRuby
def add_test_drives
if self.lesson_package_type.is_test_drive?
new_test_drives = user.remaining_test_drives + 4
new_test_drives = user.remaining_test_drives + lesson_package_type.test_drive_count
User.where(id: user.id).update_all(remaining_test_drives: new_test_drives)
user.remaining_test_drives = new_test_drives
end
@ -79,7 +79,7 @@ module JamRuby
end
if lesson_booking
purchase.lesson_package_type = lesson_booking.lesson_package_type
purchase.lesson_package_type = lesson_package_type ? lesson_package_type : lesson_booking.lesson_package_type
purchase.price = lesson_booking.booked_price if purchase.price.nil?
else
purchase.lesson_package_type = lesson_package_type

View File

@ -7,21 +7,29 @@ module JamRuby
PRODUCT_TYPE = 'LessonPackageType'
SINGLE_FREE = 'single-free'
TEST_DRIVE = 'test-drive'
TEST_DRIVE_4 = 'test-drive'
TEST_DRIVE_2 = 'test-drive-2'
TEST_DRIVE_1 = 'test-drive-1'
SINGLE = 'single'
LESSON_PACKAGE_TYPES =
[
SINGLE_FREE,
TEST_DRIVE,
TEST_DRIVE_4,
TEST_DRIVE_2,
TEST_DRIVE_1,
SINGLE
]
has_many :user_desired_packages, class_name: "JamRuby::User", :foreign_key => "lesson_package_type_id", inverse_of: :desired_package
validates :name, presence: true
validates :description, presence: true
validates :price, presence: true
validates :package_type, presence: true, inclusion: {in: LESSON_PACKAGE_TYPES}
def self.test_drive_package_ids
[TEST_DRIVE_4, TEST_DRIVE_2, TEST_DRIVE_1]
end
def self.monthly
LessonPackageType.find(MONTHLY)
end
@ -30,8 +38,16 @@ module JamRuby
LessonPackageType.find(SINGLE_FREE)
end
def self.test_drive
LessonPackageType.find(TEST_DRIVE)
def self.test_drive_4
LessonPackageType.find(TEST_DRIVE_4)
end
def self.test_drive_2
LessonPackageType.find(TEST_DRIVE_2)
end
def self.test_drive_1
LessonPackageType.find(TEST_DRIVE_1)
end
def self.single
@ -42,17 +58,21 @@ module JamRuby
if is_single_free?
0
elsif is_test_drive?
LessonPackageType.test_drive.price
10.00
elsif is_normal?
lesson_booking.booked_price #teacher.teacher.booking_price(lesson_booking.lesson_length, lesson_booking.payment_style == LessonBooking::PAYMENT_STYLE_SINGLE)
end
end
def test_drive_count
package_type["test-drive-".length, 1].to_i
end
def description(lesson_booking)
if is_single_free?
"Single Free Lesson"
elsif is_test_drive?
"Test Drive"
"Test Drive (#{test_drive_count})"
elsif is_normal?
if lesson_booking.recurring
"Recurring #{lesson_booking.payment_style == LessonBooking::PAYMENT_STYLE_WEEKLY ? "Weekly" : "Monthly"} #{lesson_booking.lesson_length}m"
@ -71,7 +91,7 @@ module JamRuby
end
def is_test_drive?
id == TEST_DRIVE
id.start_with?('test-drive')
end
def is_normal?
@ -86,8 +106,12 @@ module JamRuby
def plan_code
if package_type == SINGLE_FREE
"lesson-package-single-free"
elsif package_type == TEST_DRIVE
"lesson-package-test-drive"
elsif package_type == 'test-drive-4'
"lesson-package-test-drive-4"
elsif package_type == TEST_DRIVE_2
"lesson-package-test-drive-2"
elsif package_type == TEST_DRIVE_1
"lesson-package-test-drive-1"
elsif package_type == SINGLE
"lesson-package-single"
else

View File

@ -208,8 +208,8 @@ module JamRuby
free && non_free
end
def self.purchase_test_drive(current_user, booking = nil)
self.purchase_lesson(current_user, booking, LessonPackageType.test_drive)
def self.purchase_test_drive(current_user, lesson_package_type, booking = nil)
self.purchase_lesson(current_user, booking, lesson_package_type)
end
def self.purchase_normal(current_user, booking)
@ -273,6 +273,7 @@ module JamRuby
purchase = LessonPackagePurchase.create(current_user, lesson_booking, lesson_package_type) if purchase.nil?
if purchase.errors.any?
puts "purchase errors #{purchase.errors.inspect}"
price_info = {}
price_info[:purchase] = purchase
return price_info

View File

@ -51,7 +51,7 @@ module JamRuby
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
has_many :reviews, :class_name => "JamRuby::Review"
has_one :review_summary, :class_name => "JamRuby::ReviewSummary"
has_one :review_summary, :class_name => "JamRuby::ReviewSummary"
# calendars (for scheduling NOT in music_session)
has_many :calendars, :class_name => "JamRuby::Calendar"
@ -71,7 +71,7 @@ module JamRuby
has_many :band_musicians, :class_name => "JamRuby::BandMusician"
has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band"
has_one :teacher, :class_name => "JamRuby::Teacher"
# genres
has_many :genre_players, as: :player, class_name: "JamRuby::GenrePlayer", dependent: :destroy
has_many :genres, through: :genre_players, class_name: "JamRuby::Genre"
@ -176,6 +176,7 @@ module JamRuby
has_many :teacher_lesson_bookings, :class_name => "JamRuby::LessonBooking", :foreign_key => "teacher_id", inverse_of: :teacher
has_many :teacher_distributions, :class_name => "JamRuby::TeacherDistribution", :foreign_key => "teacher_id", inverse_of: :teacher
has_many :teacher_payments, :class_name => "JamRuby::TeacherPayment", :foreign_key => "teacher_id", inverse_of: :teacher
belongs_to :desired_package, :class_name => "JamRuby::LessonPackageType", :foreign_key => "lesson_package_type_id", inverse_of: :user_desired_packages # used to hold whether user last wanted test drive 4/2/1
# Shopping carts
has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart"
@ -260,6 +261,7 @@ module JamRuby
teacher.update_profile_pct
end
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
@ -1127,7 +1129,7 @@ module JamRuby
user.school_id = school_id
elsif user.is_a_teacher
school = School.find_by_id(school_id)
school_name = school ? school.name : 'a music school'
school_name = school ? school.name : 'a music school'
user.teacher = Teacher.build_teacher(user, validate_introduction: true, biography: "Teaches for #{school_name}", school_id: school_id)
end
else
@ -1320,7 +1322,6 @@ module JamRuby
end if affiliate_referral_id.present?
if user.is_a_student
UserMailer.student_welcome_message(user).deliver
end
@ -1897,7 +1898,7 @@ module JamRuby
end
def can_buy_test_drive?
lesson_purchases.where(lesson_package_type_id: LessonPackageType.test_drive.id).where('created_at > ?', APP_CONFIG.test_drive_wait_period_year.years.ago).count == 0
lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).where('created_at > ?', APP_CONFIG.test_drive_wait_period_year.years.ago).count == 0
end
def has_test_drives?
@ -1946,6 +1947,7 @@ module JamRuby
customer
end
def card_approved(token, zip, booking_id)
approved_booking = nil
@ -1993,6 +1995,7 @@ module JamRuby
normal = nil
intent = nil
purchase = nil
lesson_package_type = nil
User.transaction do
if params[:name].present?
@ -2004,16 +2007,29 @@ module JamRuby
booking = card_approved(params[:token], params[:zip], params[:booking_id])
if params[:test_drive]
self.reload
result = Sale.purchase_test_drive(self, booking)
if booking
lesson_package_type = booking.resolved_test_drive_package
end
if lesson_package_type.nil?
lesson_package_type = LessonPackageType.test_drive_4
end
result = Sale.purchase_test_drive(self, lesson_package_type, booking)
test_drive = result[:sale]
purchase = result[:purchase]
if booking && !purchase.errors.any?
# the booking would not have a lesson_package_purchase associated yet, so let's associate it
booking.lesson_sessions.update_all(lesson_package_purchase_id: purchase.id)
end
elsif params[:normal]
self.reload
end
end
{lesson: booking, test_drive: test_drive, purchase: purchase}
{lesson: booking, test_drive: test_drive, purchase: purchase, lesson_package_type: lesson_package_type}
end
def requested_test_drive(teacher = nil)
@ -2033,7 +2049,7 @@ module JamRuby
end
def most_recent_test_drive_purchase
lesson_purchases.where(lesson_package_type_id: LessonPackageType.test_drive.id).order('created_at desc').first
lesson_purchases.where('lesson_package_type_id in (?)', [LessonPackageType.test_drive_package_ids]).order('created_at desc').first
end
def test_drive_succeeded(lesson_session)

View File

@ -980,7 +980,7 @@ FactoryGirl.define do
price 30.00
factory :test_drive_purchase do
lesson_package_type { JamRuby::LessonPackageType.test_drive }
lesson_package_type { JamRuby::LessonPackageType.test_drive_4 }
association :lesson_booking, factory: :lesson_booking
price 49.99
end

View File

@ -20,6 +20,9 @@ describe "TestDrive Lesson Flow" do
it "works" do
user.desired_package = LessonPackageType.test_drive_2
user.save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be_false
@ -28,20 +31,24 @@ describe "TestDrive Lesson Flow" do
booking.card_presumed_ok.should be_false
booking.should eql user.unprocessed_test_drive
booking.sent_notices.should be_false
user.reload
user.remaining_test_drives.should eql 0
########## Need validate their credit card
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true})
user.reload
user.remaining_test_drives.should eql 3
result = user.payment_update({token: token, zip: '78759', test_drive: true, booking_id: booking.id})
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
test_drive = result[:test_drive]
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
lesson.errors.any?.should be_false
test_drive = result[:test_drive]
test_drive.errors.any?.should be_false
user.reload
user.remaining_test_drives.should eql 1
booking.card_presumed_ok.should be_true
booking.sent_notices.should be_true
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
@ -49,20 +56,20 @@ describe "TestDrive Lesson Flow" do
test_drive.stripe_charge_id.should_not be_nil
test_drive.recurly_tax_in_cents.should be 412
test_drive.recurly_total_in_cents.should eql 4999 + 412
test_drive.recurly_subtotal_in_cents.should eql 4999
test_drive.recurly_tax_in_cents.should be 247
test_drive.recurly_total_in_cents.should eql 2999 + 247
test_drive.recurly_subtotal_in_cents.should eql 2999
test_drive.recurly_currency.should eql 'USD'
line_item = test_drive.sale_line_items[0]
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id
line_item.product_id.should eq LessonPackageType.test_drive_2.id
user.reload
user.stripe_customer_id.should_not be nil
user.lesson_purchases.length.should eql 1
user.remaining_test_drives.should eql 3
user.remaining_test_drives.should eql 1
lesson_purchase = user.lesson_purchases[0]
lesson_purchase.price.should eql 49.99
lesson_purchase.price.should eql 29.99
lesson_purchase.lesson_package_type.is_test_drive?.should eql true
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
@ -173,11 +180,11 @@ describe "TestDrive Lesson Flow" do
lesson_session.billing_error_reason.should be_nil
lesson_session.sent_notices.should be true
purchase = lesson_session.lesson_package_purchase
purchase.should_not be nil
purchase.price_in_cents.should eql 4999
purchase.should_not be_nil
purchase.price_in_cents.should eql 2999
purchase.lesson_package_type.is_test_drive?.should be true
user.reload
user.remaining_test_drives.should eql 3
user.remaining_test_drives.should eql 1
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
teacher_distribution = lesson_session.teacher_distribution

View File

@ -731,7 +731,7 @@ describe Sale do
line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id
line_item.product_id.should eq LessonPackageType.test_drive_4.id
user.reload
user.stripe_customer_id.should_not be nil
@ -778,7 +778,7 @@ describe Sale do
line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id
line_item.product_id.should eq LessonPackageType.test_drive_4.id
user.reload
@ -819,7 +819,7 @@ describe Sale do
line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id
line_item.product_id.should eq LessonPackageType.test_drive_4.id
end
it "will reject second test drive purchase" do

View File

@ -33,7 +33,7 @@ def testdrive_lesson(user, teacher, slots = nil)
booking.card_presumed_ok.should be_true
if user.most_recent_test_drive_purchase.nil?
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive)
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
end
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
@ -66,7 +66,7 @@ def normal_lesson(user, teacher, slots = nil)
booking.card_presumed_ok.should be_true
#if user.most_recent_test_drive_purchase.nil?
# LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive)
# LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
#end
lesson.accept({message: 'Yeah I got this', slot: slots[0]})

View File

@ -161,21 +161,37 @@
return context.JK.prodBubble($element, 'teacher-profile', {}, bigHelpDarkOptions({spikeGirth:0, spikeLength: 0, duration:10000, offsetParent:$offsetParent, width:385, positions:['top', 'right', 'bottom']}))
}
helpBubble.showUseRemainingTestDrives = function($element, $offsetParent) {
return context.JK.onceBubble($element, 'side-remaining-test-drives', {}, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
helpBubble.showUseRemainingTestDrives = function($element, $offsetParent, user, callback) {
return context.JK.onceBubble($element, 'side-remaining-test-drives', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
var $bookNow = $('a.book-now')
$bookNow.off('click').on('click', function(e) {
e.preventDefault()
callback()
return false;
})
}})
}
helpBubble.showBuyTestDrive = function($element, $offsetParent) {
return context.JK.onceBubble($element, 'side-buy-test-drive', {}, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
helpBubble.showBuyTestDrive = function($element, $offsetParent, user, callback) {
return context.JK.onceBubble($element, 'side-buy-test-drive', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
var $bookNow = $('a.book-now')
$bookNow.off('click').on('click', function(e) {
e.preventDefault()
callback()
return false;
})
}})
}
helpBubble.showBuyNormalLesson = function($element, $offsetParent) {
return context.JK.onceBubble($element, 'side-buy-normal-lesson', {}, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
helpBubble.showBuyNormalLesson = function($element, $offsetParent, user, callback) {
return context.JK.onceBubble($element, 'side-buy-normal-lesson', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
var $bookNow = $('a.book-now')
$bookNow.off('click').on('click', function(e) {
e.preventDefault()
callback()
return false;
})
}})
}
})(window, jQuery);

View File

@ -62,7 +62,7 @@ UserStore = context.UserStore
@state.type == 'normal'
isTestDrive: () ->
@state.type == 'test-drive'
@state.type?.indexOf('test-drive') == 0
parseId:(id) ->
if !id?
@ -79,7 +79,6 @@ UserStore = context.UserStore
@resetErrors()
beforeShow: (e) ->
logger.debug("BookLesson: beforeShow", e.id)
afterShow: (e) ->
logger.debug("BookLesson: afterShow", e.id)
@ -268,9 +267,6 @@ UserStore = context.UserStore
onCancel: (e) ->
e.preventDefault()
isTestDrive: () ->
@state.type == 'test-drive'
isNormal: () ->
@state.type == 'normal'
@ -314,11 +310,12 @@ UserStore = context.UserStore
results
render: () ->
teacher = @state.teacher
photo_url = teacher?.photo_url
if !photo_url?
photo_url = '/assets/shared/avatar_generic.png'
teacher = @state.teacher
if teacher?
name = `<div className="teacher-name">{teacher.name}</div>`
@ -446,6 +443,39 @@ UserStore = context.UserStore
<a className={bookLessonClasses} onClick={this.onBookLesson}>BOOK TESTDRIVE LESSON</a>
</div>`
testDriveCredits = 1
if this.state.user.lesson_package_type_id == 'test-drive'
testDriveCredits = 4
else if this.state.user.lesson_package_type_id == 'test-drive-1'
testDriveCredits = 1
else if this.state.user.lesson_package_type_id == 'test-drive-2'
testDriveCredits = 2
if this.state.user.remaining_test_drives > 0
testDriveBookingInfo = `<div className="booking-info">
<p>You are booking a single 30-minute TestDrive session.</p>
<p>You currently have {testDriveLessons} available. If you need to cancel, you must cancel at least 24 hours before the lesson is scheduled to start, or you will be charged 1 TestDrive lesson credit.<br/>
<div className="jamclass-policies"><a href="/corp/terms" rel="external" onClick={this.jamclassPolicies}>jamclass
policies</a></div>
</p>
</div>`
else
testDriveBookingInfo = `<div className="booking-info">
<p>You are booking a single 30-minute TestDrive session.</p>
<p>Once payment is entered on the next screen, the teacher will be notified, and this lesson will then use 1 of {testDriveCredits} TestDrive credits. If you need to cancel, you must cancel at least 24 hours before the lesson is scheduled to start, or you will be charged 1 TestDrive lesson credit.<br/>
<div className="jamclass-policies"><a href="/corp/terms" rel="external" onClick={this.jamclassPolicies}>jamclass
policies</a></div>
</p>
</div>`
columnLeft = `<div className="column column-left">
{header}
{slots}
@ -468,15 +498,7 @@ UserStore = context.UserStore
</div>
{name}
</div>
<div className="booking-info">
<p>You are purchasing a single 30-minute TestDrive session.</p>
<p>You currently have {testDriveLessons} available. If you need to cancel, you must cancel at least 24 hours before the lesson is scheduled to start, or you will be charged 1 TestDrive lesson credit.<br/>
<div className="jamclass-policies"><a href="/corp/terms" rel="external" onClick={this.jamclassPolicies}>jamclass
policies</a></div>
</p>
</div>
{testDriveBookingInfo}
</div>`
else if @isNormal()

View File

@ -272,29 +272,58 @@ UserStore = context.UserStore
window.UserActions.refresh()
# if the response has a lesson, take them there
if response.lesson?.id?
if response.test_drive?
# ok, they bought a package
if response.lesson_package_type?
# always of form test-drive-#
prefixLength = "test-drive-".length
packageLength = response.lesson_package_type.package_type.length
logger.debug("prefix: " + prefixLength.toString())
logger.debug("package: " + packageLength.toString())
testDriveCount = response.lesson_package_type.package_type.substring(prefixLength, packageLength)
logger.debug("testDriveCount: " + testDriveCount)
if response.test_drive?.teacher_id
teacher_id = response.test_drive.teacher_id
if testDriveCount == 1
text = "You have purchased a TestDrive credit and have used it to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
else
text = "You have purchased #{testDriveCount} TestDrive credits and have used 1 credit it to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
location = "/client#/jamclass"
else
if @state.teacher?.id
# the user bought the testdrive, and there is a teacher of interest in context (but no booking)
if testDriveCount == 1
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the lesson booking screen for the teacher you initially showed interest in."
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
else
text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.<br/><br/>We've taken you to the lesson booking screen for the teacher you initially showed interest in."
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
else
# the user bought test drive, but 'cold' , i.e., no teacher in context
if testDriveCount == 1
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the Teacher Search screen, so you can search for teachers right for you."
location = "/client#/teachers/search"
else
text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.<br/><br/>We've taken you to the Teacher Search screen, so you can search for teachers right for you."
location = "/client#/teachers/search"
context.JK.Banner.showNotice("Test Drive Purchased",text)
window.location = location
else
context.JK.Banner.showNotice("Something Went Wrong", "Please email support@jamkazam.com and indicate that your attempt to buy a TestDrive failed")
window.location = "/client#/jamclass/"
else if response.lesson?.id?
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.<br/><br/>We've taken you automatically to the page for this request, and sent an email to you with a link here as well. All communication with the teacher will show up on this page and in email.")
url = "/client#/jamclass/lesson-booking/" + response.lesson.id
url = "/client#/jamclass"
window.location.href = url
else if response.test_drive?
if response.test_drive?.teacher_id
teacher_id = response.test_drive.teacher_id
else if @state.teacher?.id
teacher_id = @state.teacher.id
if teacher_id?
text = "You now have 4 lessons that you can take with 4 different teachers.<br/><br/>We've taken you automatically to the lesson booking screen for the teacher you initially showed interest in."
location = "/client#/profile/teacher/" + teacher_id
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
else
text = "You now have 4 lessons that you can take with 4 different teachers.<br/><br/>We've taken you automatically to the Teacher Search screen, so you can search for teachers right for you."
location = "/client#/teachers/search"
context.JK.Banner.showNotice("Test Drive Purchased",text)
window.location = location
else
window.location = "/client#/teachers/search"

View File

@ -27,6 +27,7 @@ proficiencyDescriptionMap = {
@TeacherProfile = React.createClass({
mixins: [
PostProcessorMixin,
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(UserStore, "onUserChanged"),
Reflux.listenTo(SubjectStore, "onSubjectsChanged"),
@ -89,12 +90,18 @@ proficiencyDescriptionMap = {
userDetailDone: (response) ->
if response.id == @state.userId
console.log("teacher markup", response)
@postProcessUser(response)
@setState({user: response, isSelf: response.id == context.JK.currentUserId})
else
logger.debug("ignoring userDetailDone", response.id, @state.userId)
if @visible
@showSideBubble()
@showSideBubbleWhenReady()
showSideBubbleWhenReady: () ->
if @user? && @state.user?
if @visible
@showSideBubble()
beforeHide: (e) ->
@visible = false
@ -130,18 +137,22 @@ proficiencyDescriptionMap = {
@screen.btOff()
showUseRemainingTestDrivesBubble: ( ) ->
console.log("Ok showUseRemainingTestDrivesBubble")
context.JK.HelpBubbleHelper.showUseRemainingTestDrives(@screen, @screen)
context.JK.HelpBubbleHelper.showUseRemainingTestDrives(@screen, @screen, @user, (() => @useRemainingTestDrives()))
showBuyTestDriveBubble: () ->
console.log("ok showBuyTestDriveBubble")
context.JK.HelpBubbleHelper.showBuyTestDrive(@screen, @screen)
context.JK.HelpBubbleHelper.showBuyTestDrive(@screen, @screen, @user, (() => @buyTestDrive()))
showBuyNormalLessonBubble: () ->
console.log("OK showBuyNormalLessonBubble")
context.JK.HelpBubbleHelper.showBuyNormalLesson(@screen, @screen)
context.JK.HelpBubbleHelper.showBuyNormalLesson(@screen, @screen, @state.user, (() => @buyNormalLesson()))
useRemainingTestDrives: () ->
window.location.href = "/client#/jamclass/book-lesson/test-drive_#{@state.user.id}"
buyTestDrive: () ->
window.location.href = "/client#/jamclass/test-drive-selection/#{@state.user.id}"
buyNormalLesson: () ->
window.location.href = "/client#/jamclass/book-lesson/normal_#{@state.user.id}"
getInitialState: () ->
{
@ -157,6 +168,7 @@ proficiencyDescriptionMap = {
onUserChanged: (userState) ->
@user = userState?.user
@showSideBubbleWhenReady()
editProfile: (selected, e) ->
e.preventDefault()

View File

@ -27,7 +27,7 @@ ProfileActions = @ProfileActions
refreshing: false
getInitialState: () ->
{searchOptions: {}, results: []}
{searchOptions: {}, results: [], user: null}
onAppInit: (@app) ->
@app.bindScreen('teachers/search', {beforeShow: @beforeShow, afterShow: @afterShow, afterHide: @afterHide})
@ -59,6 +59,7 @@ ProfileActions = @ProfileActions
@setState(results)
onUserChanged: (@user) ->
@setState({user: @user?.user})
#onTeacherSearchChanged: (options) ->
# if @visible
@ -119,7 +120,8 @@ ProfileActions = @ProfileActions
.done((response) =>
if response.remaining_test_drives == 0 && response['can_buy_test_drive?']
logger.debug("TeacherSearchScreen: user offered test drive")
@app.layout.showDialog('try-test-drive', {d1: user.teacher.id})
#@app.layout.showDialog('try-test-drive', {d1: user.teacher.id})
window.location.href = '/client#/jamclass/test-drive-selection/' + user.id
else if response.remaining_test_drives > 0
if response.booked_with_teacher && !context.JK.currentUserAdmin
logger.debug("TeacherSearchScreen: teacher already test-drived")
@ -130,6 +132,7 @@ ProfileActions = @ProfileActions
logger.debug("TeacherSearchScreen: user being sent to book a lesson")
window.location.href = '/client#/jamclass/book-lesson/test-drive_' + user.id
#window.location.href = '/client#/jamclass/test-drive-selection/' + user.id
else
# user has no remaining test drives and can't buy any
logger.debug("TeacherSearchScreen: test drive all done")
@ -230,6 +233,11 @@ ProfileActions = @ProfileActions
if !bio?
bio = 'No bio'
console.log("@state.sur : #{@state.user.remaining_test_drives}, #{@state.user['can_buy_test_drive?']}")
if !@state.user? || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?']
bookTestDriveBtn = `<a className="button-orange try-test-drive" onClick={this.bookTestDrive.bind(this, user)}>BOOK TESTDRIVE LESSON</a>`
else
bookSingleBtn = `<a className="button-orange try-normal" onClick={this.bookNormalLesson.bind(this, user)}>BOOK LESSON</a>`
resultsJsx.push(`<div key={user.id} className="teacher-search-result" data-teacher-id={user.id}>
<div className="user-avatar">
<div className="avatar small">
@ -246,8 +254,8 @@ ProfileActions = @ProfileActions
</div>
<div className="teacher-actions">
<a className="button-orange more-about-teacher" onClick={this.moreAboutTeacher.bind(this, user)}>MORE ABOUT THIS TEACHER</a>
<a className="button-orange try-test-drive" onClick={this.bookTestDrive.bind(this, user)}>BOOK TESTDRIVE LESSON</a>
<a className="button-orange try-normal" onClick={this.bookNormalLesson.bind(this, user)}>BOOK NORMAL LESSON</a>
{bookTestDriveBtn}
{bookSingleBtn}
</div>
</div>
<br className="clearall" />

View File

@ -0,0 +1,174 @@
context = window
rest = context.JK.Rest()
logger = context.JK.logger
UserStore = context.UserStore
@TestDriveSelection = React.createClass({
mixins: [
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(UserStore, "onUserChanged")
]
onAppInit: (@app) ->
@app.bindScreen('jamclass/test-drive-selection',
{afterShow: @afterShow, beforeHide: @beforeHide, navName: 'TestDrive Selection'})
onUserChanged: (userState) ->
@setState({user: userState?.user})
beforeHide: (e) ->
afterShow: (e) ->
logger.debug("TestDriveSelection: afterShow", e.id)
parsed = @parseId(e.id)
id = parsed.id
if id? && id != 'none' && id != 'default'
@setState({teacherId: id, teacher: null})
else
@setState({teacherId: null, teacher: null})
rest.getUserDetail({
id: id,
show_teacher: true
}).done((response) => @userDetailDone(response)).fail(@app.ajaxError)
parseId: (id) ->
if !id?
{id: null}
else
{id: id}
userDetailDone: (response) ->
if response.id == @state.teacherId
@setState({teacher: response, isSelf: response.id == context.JK.currentUserId})
else
logger.debug("TestDriveSelection: ignoring teacher details", response.id, @state.teacherId)
getInitialState: () ->
{
user: null,
teacher: null,
teacherId: null,
}
packageSelect: (packageType, e) ->
e.preventDefault()
console.log("test-drive-#{packageType}")
rest.updateUser({desired_package: "test-drive-#{packageType}"}).done((response) => @packageSelectedDone(response)).fail((jqXHR) => @packageSelectedFail(jqXHR))
packageSelectedFail: (jqXHR) ->
console.log("package selected fail: " + jqXHR.responseText)
@app.ajaxError(jqXHR)
packageSelectedDone:(response) ->
url = "/client#/jamclass/book-lesson/test-drive"
console.log("TEACHER", JSON.stringify(@state.teacher))
if @state.teacher?
url += '_' + @state.teacher.id
else
url = "/client#/jamclass/lesson-payment/test-drive"
window.location.href = url
avatar: (name = 'your choice', photo_url = '/assets/shared/avatar_generic.png') ->
`<div className="avatar-header">
<div className="avatar">
<img src={photo_url}/>
</div>
</div>`
render: () ->
teacher_name = @state.teacher?.name
photo_url = @state.teacher?.photo_url
if !photo_url?
photo_url = '/assets/shared/avatar_generic.png'
`<div className="content-body-scroller">
<Nav/>
<div className="selection-area">
<div className="test-drive-selection-wrapper select-4">
<div className="test-drive-selection">
<div className="td-header">MOST POPULAR</div>
<div className="td-content">
<div className="avatars">
{this.avatar(teacher_name, photo_url)}
{this.avatar()}
{this.avatar()}
{this.avatar()}
</div>
<div className="td-msg">
GET 4 LESSONS WITH 4 DIFFERENT TEACHERS FOR JUST $12.50 EACH
</div>
<div className="td-desc">
You wouldn't marry the first person you date - right? Choosing the right teacher is the most important
thing you can do to ensure success and become a better musician. Try 4 different teachers. Then pick the
one
who is best for YOU!
</div>
</div>
</div>
<a className="button-orange select-4 select-package" onClick={this.packageSelect.bind(this, '4')}>SELECT</a>
<div className="price-notice">Just $49.99!</div>
</div>
<div className="test-drive-selection-wrapper select-2">
<div className="test-drive-selection">
<div className="td-header">GREAT VALUE</div>
<div className="td-content">
<div className="avatars">
{this.avatar(teacher_name, photo_url)}
{this.avatar()}
</div>
<div className="td-msg">
GET 2 LESSONS<br/>FOR THE PRICE OF 1
</div>
<div className="td-desc">
Want to try more than one teacher, but 4 is too many for you? Try two lessons with two different
teachers for the price of one lesson.
A great value, and a good way to find an excellent teacher!
</div>
</div>
</div>
<a className="button-orange select-2 select-package" onClick={this.packageSelect.bind(this, '2')}>SELECT</a>
<div className="price-notice">Just $29.99!</div>
</div>
<div className="test-drive-selection-wrapper select-1">
<div className="test-drive-selection">
<div className="td-header">I'M SURE</div>
<div className="td-content">
<div className="avatars">
{this.avatar(teacher_name, photo_url)}
</div>
<div className="td-msg">
GET 1 HALF-PRICE LESSON<br/>TO GET STARTED
</div>
<div className="td-desc">
Are you confident you've found the best teacher for you? Then book your first lesson at a terrific
value, and get your first lesson scheduled to start learning more today!
</div>
</div>
</div>
<a className="button-orange select-1 select-package" onClick={this.packageSelect.bind(this, '1')}>SELECT</a>
<div className="price-notice">Just $14.99!</div>
</div>
<br className="clearall"/>
</div>
</div>`
})

View File

@ -19,7 +19,6 @@ teacherActions = window.JK.Actions.Teacher
lesson.isAdmin = context.JK.currentUserAdmin
lesson.cardNotOk = !lesson.lesson_booking.card_presumed_ok
console.log("lesson.isAdmin",lesson.isAdmin )
if (lesson.status == 'requested' || lesson.status == 'countered')
lesson.isRequested = true
if lesson.cardNotOk
@ -56,4 +55,40 @@ teacherActions = window.JK.Actions.Teacher
user.musician_profile = '/client#/profile/' + user.id
user.best_profile = user.musician_profile
if user.is_a_teacher
cheapest_lesson_stmt = '(no pricing set yet)'
lowestPrice = null
lowestDuration = null
single = true
enabledMinutes = []
for minutes in [30, 45, 60, 90, 120]
duration_enabled = user.teacher["lesson_duration_#{minutes}"]
if duration_enabled
enabledMinutes.push(minutes)
if user.teacher.prices_per_lesson
for minutes in enabledMinutes
lesson_price = user.teacher["price_per_lesson_#{minutes}_cents"]
if lesson_price?
if !lowestPrice? || lesson_price < lowestPrice
lowestPrice = lesson_price
single = true
lowestDuration = minutes
for minutes in enabledMinutes
lesson_price = user.teacher["price_per_month_#{minutes}_cents"]
if lesson_price?
if !lowestPrice? || (lesson_price / 4) < lowestPrice
lowestPrice = lesson_price / 4
single = false
lowestDuration = minutes
if lowestPrice?
if single
# lowest price appears to be a single lesson
cheapest_lesson_stmt = "$#{lowestPrice / 100} for #{lowestDuration} minutes"
else
# lowest price appears to be a monthly recurring lesson
cheapest_lesson_stmt = "$#{Math.round(lowestPrice * 4) / 100} per month"
user.cheapest_lesson_stmt = cheapest_lesson_stmt
}

View File

@ -143,8 +143,13 @@
options = {};
}
var originalPostShow = options.postShow;
options.postShow = function(container) {
context.JK.popExternalLinks($(container))
if (originalPostShow) {
originalPostShow(container);
}
}

View File

@ -64,22 +64,6 @@ $labelFontSize: 12px;
}
select {
border: 0 !important; /*Removes border*/
-webkit-appearance: none; /*Removes default chrome and safari style*/
-moz-appearance: none; /* Removes Default Firefox style*/
appearance: none;
background: url('/assets/down_arrow_black_pad.png') no-repeat; /*Adds background-image*/
background-position: right center; /*Position of the background-image*/
text-indent: 0.01px; /* Removes default arrow from firefox*/
text-overflow: ""; /*Removes default arrow from firefox*/
cursor: pointer;
padding-right:20px;
&::-ms-expand {
display: none;
}
}
@mixin border_box_sizing {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;

View File

@ -99,6 +99,7 @@ body.jam, body.web, .dialog{
}
p {
margin:.8em 1em;
line-height:120%;
}
.book-now {
width:100px;

View File

@ -76,8 +76,21 @@ a.arrow-down {
}
select {
padding:3px;
font-size:15px;
border: 0 !important; /*Removes border*/
-webkit-appearance: none; /*Removes default chrome and safari style*/
-moz-appearance: none; /* Removes Default Firefox style*/
appearance: none;
background: #C5C5C5 url('/assets/down_arrow_black_pad.png') no-repeat; /*Adds background-image*/
background-position: right center; /*Position of the background-image*/
text-indent: 0.01px; /* Removes default arrow from firefox*/
text-overflow: ""; /*Removes default arrow from firefox*/
cursor: pointer;
padding:3px 20px 3px 3px;
&::-ms-expand {
display: none;
}
font-size:15px;
border-radius: 6px;
}

View File

@ -16,6 +16,20 @@
.column-left {
float:left;
padding-right:20px;
.slot.slot-2 {
border-style: solid;
border-width: 1px 0 0 0;
border-color: #cccccc;
padding-top: 20px;
}
.description {
border-style: solid;
border-width: 1px 0 0 0;
border-color: #cccccc;
padding-top: 20px;
}
}
.column-right {
float:right;
@ -56,7 +70,7 @@
textarea {
width:100%;
@include border_box_sizing;
height:125px;
height:75px;
}
.field {
display:inline-block;
@ -99,7 +113,7 @@
}
.jamclass-policies {
text-align:center;
margin-top:-20px;
margin-top:-10px;
}
.actions {
margin-left:-3px;
@ -138,7 +152,7 @@
width:80%;
}
}
select.hour {
margin-left:20px;
select.am_pm {
margin-left:13px;
}
}

View File

@ -0,0 +1,164 @@
@import "client/common";
$fluid-break: 1335px;
@mixin layout-small {
@media (max-width: #{$fluid-break - 1px}) {
@content;
}
}
@mixin layout-normal {
@media (min-width: #{$fluid-break}) {
@content;
}
}
#test-drive-selection {
div[data-react-class="TestDriveSelection"] {
height: 100%;
}
.content-body-scroller {
height: 100%;
padding: 30px;
@include border_box_sizing;
}
h2 {
font-size: 24px;
font-weight: 700;
margin-bottom: 20px !important;
display: inline-block;
}
.test-drive-selection-wrapper {
float:left;
text-align:center;
width:31%;
&.select-4 {
}
&.select-2 {
margin:0 3.5%;
}
&.select-1 {
}
}
.test-drive-selection {
display:inline-block;
border-radius:10px;
border-width:1px;
border-color:$ColorScreenPrimary;
border-style:solid;
margin-bottom:15px;
@media (max-width: 1180px) {
min-height:360px;
}
@media (min-width: 1181px) {
min-height:340px;
}
@media (min-width: 1450px) {
min-height:305px;
}
}
a.select-package {
margin-bottom:15px;
max-width: 140px;
width: 100%;
}
.price-notice {
color:white;
font-size:16px;
}
.td-header {
height:45px;
color:white;
text-align:center;
font-size:24px;
line-height: 45px;
vertical-align: middle;
background-color:$ColorScreenPrimary;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
@media (max-width: 1130px) {
font-size:16px;
}
}
.td-content {
@include border-box_sizing;
padding:10px;
width:100%;
}
.avatars {
margin-bottom:20px;
}
.td-msg {
margin-bottom:20px;
@media (max-width: 1400px) {
min-height:42px;
}
}
.td-desc {
line-height:120%;
color:$ColorTextTypical;
}
.avatar-header {
display:inline-block;
}
.avatar {
display:inline-block;
padding:1px;
width:48px;
height:48px;
background-color:#ed4818;
margin:10px 8px 0 0;
-webkit-border-radius:24px;
-moz-border-radius:24px;
border-radius:24px;
float:none;
@include layout-small {
width:32px;
height:32px;
-webkit-border-radius:16px;
-moz-border-radius:16px;
border-radius:16px;
margin:10px 6px 0 0;
}
}
.avatar img {
width: 48px;
height: 48px;
-webkit-border-radius:24px;
-moz-border-radius:24px;
border-radius:24px;
@include layout-small {
width:32px;
height:32px;
-webkit-border-radius:16px;
-moz-border-radius:16px;
border-radius:16px;
}
}
.selection-area {
width:100%;
padding-top:20px;
}
}

View File

@ -14,6 +14,7 @@ class ApiStripeController < ApiController
@lesson = data[:lesson]
@test_drive = data[:test_drive]
@normal = data[:normal]
@lesson_package_type = data[:lesson_package_type]
end
end

View File

@ -1,6 +1,5 @@
require 'sanitize'
class
ApiUsersController < ApiController
class ApiUsersController < ApiController
before_filter :api_signed_in_user, :except => [:create, :calendar, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring, :add_play, :crash_dump, :validate_data, :google_auth, :user_event]
before_filter :auth_user, :only => [:session_settings_show, :session_history_index, :session_user_history_index, :update, :delete, :authorizations, :test_drive_status,
@ -178,6 +177,7 @@ ApiUsersController < ApiController
@user.is_a_student = params[:student] if params.has_key?(:student)
@user.is_a_teacher = params[:teacher] if params.has_key?(:teacher)
@user.school_interest = !!params[:school_interest]
@user.desired_package = LessonPackageType.find_by_package_type!(params[:desired_package]) if params.has_key?(:desired_package)
@user.save
if @user.errors.any?

View File

@ -9,16 +9,21 @@ end
if @test_drive
node :test_drive do |lesson|
attributes :teacher_id
{teacher_id: @test_drive.id}
end
end
if @normal
node :normal do |lesson|
attributes :teacher_id
{teacher_id: @normal.teacher_id}
end
end
if @lesson_package_type
node :lesson_package_type do |lesson_package_type|
{package_type: @lesson_package_type.package_type}
end
end

View File

@ -28,7 +28,7 @@ end
# give back more info if the user being fetched is yourself
if current_user && @user == current_user
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count, :first_downloaded_client_at, :created_at, :first_opened_jamtrack_web_player, :gifted_jamtracks, :has_redeemable_jamtrack, :remaining, :has_stored_credit_card?, :remaining_test_drives, :can_buy_test_drive?
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count, :first_downloaded_client_at, :created_at, :first_opened_jamtrack_web_player, :gifted_jamtracks, :has_redeemable_jamtrack, :remaining, :has_stored_credit_card?, :remaining_test_drives, :can_buy_test_drive?, :lesson_package_type_id
node :owned_school_id do |user|
user.owned_school.id if user.owned_school

View File

@ -403,6 +403,7 @@ script type="text/template" id="template-help-teacher-profile"
script type="text/template" id="template-help-side-remaining-test-drives"
.side-remaining-test-drives
h2 Book TestDrive Lesson
p You currently have {{data.remaining_test_drives}} TestDrive lesson credits available.
a.book-now.button-orange BOOK NOW!
@ -418,7 +419,7 @@ script type="text/template" id="template-help-side-buy-test-drive"
script type="text/template" id="template-help-side-buy-normal-lesson"
.side-buy-normal-lesson
h2 Book Lesson
p Lessons with {{data.first_name}} start at just ${{data.cheapest_lesson_stmt}}.
p Lessons with {{data.first_name}} start at just {{data.cheapest_lesson_stmt}}.
a.book-now.button-orange BOOK NOW!
p Or call us at
p 877-376-8742 (877-37-MUSIC)

View File

@ -50,6 +50,7 @@
<%= render "clients/jamclass/lesson_payment" %>
<%= render "clients/jamclass/lesson_session" %>
<%= render "clients/jamclass/lesson_booking" %>
<%= render "clients/jamclass/test_drive_selection" %>
<%= render "clients/jamclass/jamclass_student" %>
<%= render "users/feed_music_session_ajax" %>
<%= render "users/feed_recording_ajax" %>

View File

@ -1,7 +1,7 @@
#lesson-booking.screen.secondary layout="screen" layout-id="jamclass/lesson-booking" layout-arg="id"
.content-head
.content-icon
= image_tag "content/icon_account.png", :size => "27x20"
= image_tag "content/icon_musicians.png", :size => "20x20"
h1
| jamclass
= render "screen_navigation"

View File

@ -0,0 +1,10 @@
#test-drive-selection.screen.secondary layout="screen" layout-id="jamclass/test-drive-selection" layout-arg="id"
.content-head
.content-icon
= image_tag "content/icon_musicians.png", :size => "20x20"
h1
| jamclass
= render "screen_navigation"
.content-body
= react_component 'TestDriveSelection', {}

View File

@ -71,7 +71,7 @@ namespace :lessons do
lesson = booking.lesson_sessions[0]
if user.most_recent_test_drive_purchase.nil?
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive)
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
end
#lesson.accept({message: 'Yeah I got this', slot: slots[0]})

View File

@ -950,7 +950,7 @@ FactoryGirl.define do
price 30.00
factory :test_drive_purchase do
lesson_package_type { JamRuby::LessonPackageType.test_drive }
lesson_package_type { JamRuby::LessonPackageType.test_drive_4 }
association :lesson_booking, factory: :lesson_booking
price 49.99
end

View File

@ -7,6 +7,7 @@ describe "Book Monthly Recurring Lesson", :js => true, :type => :feature, :capyb
let(:user) { FactoryGirl.create(:user, traditional_band: true,paid_sessions: true, paid_sessions_hourly_rate: 1, paid_sessions_daily_rate:1 ) }
let(:teacher_user) {FactoryGirl.create(:teacher_user, first_name: "Teacher1", ready_for_session_at: Time.now)}
let(:teacher_user2) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
let(:teacher_user3) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
after(:each) do
Timecop.return
@ -23,7 +24,12 @@ describe "Book Monthly Recurring Lesson", :js => true, :type => :feature, :capyb
UserMailer.deliveries.clear
emulate_client
sign_in_poltergeist user
# create an old test drive and fakely use up all the credits so that we can book the lesson
Timecop.travel(Date.new(2016, 03, 01))
testdrive_lesson(user, teacher_user3)
user.remaining_test_drives = 0
user.save!
user.reload
teacher_user.teacher.ready_for_session_at = Time.now
@ -38,10 +44,11 @@ describe "Book Monthly Recurring Lesson", :js => true, :type => :feature, :capyb
it "succeeds" do
visit "/client#/teachers/search"
Timecop.travel(Date.new(2016, 04, 01))
# let's do a time half-way into the month, so we can prove some pro-rating
Timecop.travel(Date.new(2016, 04, 15))
sign_in_poltergeist user
visit "/client#/teachers/search"
find('.teacher-search-result[data-teacher-id="' + teacher_user.id + '"] .try-normal').trigger(:click)
@ -61,32 +68,35 @@ describe "Book Monthly Recurring Lesson", :js => true, :type => :feature, :capyb
find('a.book-lesson-btn', text: 'BOOK LESSON').trigger(:click)
find('h2', text: 'enter payment info for lesson')
#find('h2', text: 'enter payment info for lesson')
user.student_lesson_bookings.count.should eql 1
lesson_booking = user.student_lesson_bookings.first
lesson_booking.is_requested?.should be_true
lesson_booking.card_presumed_ok.should be_false
lesson_booking.recurring.should be true
lesson_booking.is_monthly_payment?.should be true
fill_in 'card-number', with: '4111111111111111'
fill_in 'expiration', with: '11/2016'
fill_in 'cvv', with: '111'
fill_in 'zip', with: '78759'
#fill_in 'card-number', with: '4111111111111111'
#fill_in 'expiration', with: '11/2016'
#fill_in 'cvv', with: '111'
#fill_in 'zip', with: '78759'
find('.purchase-btn').trigger(:click)
#find('.purchase-btn').trigger(:click)
# we tell user they have test drive purchased, and take them to the teacher screen
find('#banner h1', text: 'Lesson Requested')
# dismiss banner
find('a.button-orange', text:'CLOSE').trigger(:click)
user.student_lesson_bookings.count.should eql 2
lesson_booking = user.student_lesson_bookings.order(:created_at).last
lesson_booking.is_requested?.should be_true
lesson_booking.card_presumed_ok.should be_true
lesson_booking.recurring.should be true
lesson_booking.is_monthly_payment?.should be true
lesson_booking = LessonBooking.where(teacher_id: teacher_user).first
lesson_booking.should_not be_nil
lesson_session = LessonSession.where(teacher_id: teacher_user).first
lesson_session.teacher.should eql teacher_user
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id, teacher_id: teacher_user.id).first
lesson_package_purchase.should be_nil
user.reload
user.remaining_test_drives.should eql 0
@ -150,7 +160,7 @@ describe "Book Monthly Recurring Lesson", :js => true, :type => :feature, :capyb
lesson_booking.recurring.should be false
LessonSession.where(teacher_id: teacher_user2).count.should eql 1
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id, teacher_id: teacher_user.id).first
lesson_package_purchase.should be_nil
user.reload
user.remaining_test_drives.should eql 0
@ -176,13 +186,13 @@ describe "Book Monthly Recurring Lesson", :js => true, :type => :feature, :capyb
lesson_session1.billed.should eql false
lesson_session1.success.should be_true
user.lesson_purchases.count.should eql 0
user.lesson_purchases.count.should eql 1
LessonBooking.hourly_check
user.reload
lesson_package_purchase = user.lesson_purchases.count.should eql 1
lesson_package_purchase = user.lesson_purchases.first
lesson_package_purchase = user.lesson_purchases.count.should eql 2
lesson_package_purchase = user.lesson_purchases.where(teacher_id: teacher_user.id).last
teacher_distribution = lesson_package_purchase.teacher_distribution
teacher_distribution.amount_in_cents.should eql 3000 / 2
teacher_distribution.ready.should be_true

View File

@ -7,6 +7,7 @@ describe "Book Single Recurring Lesson", :js => true, :type => :feature, :capyba
let(:user) { FactoryGirl.create(:user, traditional_band: true,paid_sessions: true, paid_sessions_hourly_rate: 1, paid_sessions_daily_rate:1 ) }
let(:teacher_user) {FactoryGirl.create(:teacher_user, first_name: "Teacher1", ready_for_session_at: Time.now)}
let(:teacher_user2) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
let(:teacher_user3) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
after(:each) do
Timecop.return
@ -23,8 +24,17 @@ describe "Book Single Recurring Lesson", :js => true, :type => :feature, :capyba
UserMailer.deliveries.clear
emulate_client
sign_in_poltergeist user
# create an old test drive and fakely use up all the credits so that we can book the lesson
Timecop.travel(Date.new(2016, 03, 01))
testdrive_lesson(user, teacher_user3)
user.remaining_test_drives = 0
user.save!
user.reload
teacher_user.teacher.ready_for_session_at = Time.now
teacher_user.teacher.save!
teacher_user.teacher.price_per_lesson_60_cents.should eql 3000
Teacher.index(user, {})[:query].count.should eql 1
teacher_user.teacher.ready_for_session_at = Time.now
teacher_user.teacher.save!
@ -38,10 +48,13 @@ describe "Book Single Recurring Lesson", :js => true, :type => :feature, :capyba
it "succeeds" do
visit "/client#/teachers/search"
Timecop.travel(Date.new(2016, 04, 01))
sign_in_poltergeist user
visit "/client#/teachers/search"
find('.teacher-search-result[data-teacher-id="' + teacher_user.id + '"] .try-normal').trigger(:click)
@ -60,32 +73,35 @@ describe "Book Single Recurring Lesson", :js => true, :type => :feature, :capyba
find('a.book-lesson-btn', text: 'BOOK LESSON').trigger(:click)
find('h2', text: 'enter payment info for lesson')
# find('h2', text: 'enter payment info for lesson')
user.student_lesson_bookings.count.should eql 1
lesson_booking = user.student_lesson_bookings.first
lesson_booking.is_requested?.should be_true
lesson_booking.card_presumed_ok.should be_false
lesson_booking.recurring.should be true
lesson_booking.is_monthly_payment?.should be false
fill_in 'card-number', with: '4111111111111111'
fill_in 'expiration', with: '11/2016'
fill_in 'cvv', with: '111'
fill_in 'zip', with: '78759'
#fill_in 'card-number', with: '4111111111111111'
#fill_in 'expiration', with: '11/2016'
#fill_in 'cvv', with: '111'
#fill_in 'zip', with: '78759'
find('.purchase-btn').trigger(:click)
#find('.purchase-btn').trigger(:click)
# we tell user they have test drive purchased, and take them to the teacher screen
find('#banner h1', text: 'Lesson Requested')
# dismiss banner
find('a.button-orange', text:'CLOSE').trigger(:click)
user.student_lesson_bookings.count.should eql 2
lesson_booking = user.student_lesson_bookings.order(:created_at).last
lesson_booking.is_requested?.should be_true
lesson_booking.card_presumed_ok.should be_true
lesson_booking.recurring.should be true
lesson_booking.is_monthly_payment?.should be false
lesson_booking = LessonBooking.where(teacher_id: teacher_user).first
lesson_booking.should_not be_nil
lesson_session = LessonSession.where(teacher_id: teacher_user).first
lesson_session.teacher.should eql teacher_user
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id, teacher_id: teacher_user.id).first
lesson_package_purchase.should be_nil
user.reload
user.remaining_test_drives.should eql 0
@ -149,7 +165,7 @@ describe "Book Single Recurring Lesson", :js => true, :type => :feature, :capyba
lesson_booking.recurring.should be false
LessonSession.where(teacher_id: teacher_user2).count.should eql 1
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id, teacher_id: teacher_user.id).first
lesson_package_purchase.should be_nil
user.reload
user.remaining_test_drives.should eql 0
@ -175,7 +191,7 @@ describe "Book Single Recurring Lesson", :js => true, :type => :feature, :capyba
lesson_session1.billed.should eql true
lesson_session1.success.should be_true
lesson_session1.lesson_payment_charge.billed.should be_true
lesson_session1.lesson_payment_charge.amount_in_cents.should eql 3000
lesson_session1.lesson_payment_charge.amount_in_cents.should eql (3000 + (3000 * 0.0825).round)
lesson_session1.lesson_payment_charge.fee_in_cents.should eql 0
lesson_session1.lesson_payment_charge.stripe_charge_id.should_not be_nil
lesson_session1.lesson_payment_charge.post_processed.should be_true

View File

@ -7,6 +7,7 @@ describe "Single Lesson", :js => true, :type => :feature, :capybara_feature => t
let(:user) { FactoryGirl.create(:user, traditional_band: true,paid_sessions: true, paid_sessions_hourly_rate: 1, paid_sessions_daily_rate:1 ) }
let(:teacher_user) {FactoryGirl.create(:teacher_user, first_name: "Teacher1", ready_for_session_at: Time.now)}
let(:teacher_user2) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
let(:teacher_user3) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
before(:each) do
@ -17,14 +18,16 @@ describe "Single Lesson", :js => true, :type => :feature, :capybara_feature => t
UserMailer.deliveries.clear
emulate_client
# create an old test drive and fakely use up all the credits so that we can book the lesson
Timecop.travel(Date.new(2016, 03, 01))
testdrive_lesson(user, teacher_user3)
user.remaining_test_drives = 0
user.save!
user.reload
sign_in_poltergeist user
teacher_user.teacher.ready_for_session_at = Time.now
teacher_user.teacher.save!
teacher_user.teacher.price_per_lesson_60_cents.should eql 3000
Teacher.index(user, {})[:query].count.should eql 1
@ -62,33 +65,35 @@ describe "Single Lesson", :js => true, :type => :feature, :capybara_feature => t
#find('h2', text: 'your lesson has been requested')
find('h2', text: 'enter payment info for lesson')
user.student_lesson_bookings.count.should eql 1
lesson_booking = user.student_lesson_bookings.first
lesson_booking.is_requested?.should be_true
lesson_booking.card_presumed_ok.should be_false
lesson_booking.recurring.should be false
#find('h2', text: 'enter payment info for lesson')
#fill_in 'card-number', with: '4111111111111111'
#fill_in 'expiration', with: '11/2016'
#fill_in 'cvv', with: '111'
#fill_in 'zip', with: '78759'
fill_in 'card-number', with: '4111111111111111'
fill_in 'expiration', with: '11/2016'
fill_in 'cvv', with: '111'
fill_in 'zip', with: '78759'
find('.purchase-btn').trigger(:click)
#find('.purchase-btn').trigger(:click)
# we tell user they have test drive purchased, and take them to the teacher screen
find('#banner h1', text: 'Lesson Requested')
# dismiss banner
find('a.button-orange', text:'CLOSE').trigger(:click)
lesson_booking = LessonBooking.where(teacher_id: teacher_user).first
user.student_lesson_bookings.count.should eql 2 # this single one, and the test drive created in the before section of the test
lesson_booking = user.student_lesson_bookings.order(:created_at).last
lesson_booking.is_requested?.should be_true
lesson_booking.card_presumed_ok.should be_true
lesson_booking.recurring.should be false
lesson_booking = LessonBooking.where(teacher_id: teacher_user).order(:created_at).last
lesson_booking.should_not be_nil
lesson_session = LessonSession.where(teacher_id: teacher_user).first
lesson_session = LessonSession.where(teacher_id: teacher_user).order(:created_at).last
lesson_session.teacher.should eql teacher_user
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id, teacher_id: teacher_user.id).order(:created_at).first
lesson_package_purchase.should be_nil
user.reload
user.remaining_test_drives.should eql 0
@ -151,7 +156,7 @@ describe "Single Lesson", :js => true, :type => :feature, :capybara_feature => t
lesson_session.teacher.should eql teacher_user2
lesson_session2 = lesson_session
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id, teacher_id: teacher_user.id).order(:created_at).first
lesson_package_purchase.should be_nil
user.reload
user.remaining_test_drives.should eql 0
@ -167,9 +172,9 @@ describe "Single Lesson", :js => true, :type => :feature, :capybara_feature => t
lesson_session1.analysed.should be_true
analysis = JSON.parse(lesson_session1.analysis)
analysis["reason"].should eql LessonSessionAnalyser::SUCCESS
lesson_session1.billing_attempts.should be_true
lesson_session1.billed.should eql true
lesson_session1.success.should be_true
lesson_session1.billing_attempts.should eql 1
lesson_session1.billed.should eql true
LessonBooking.hourly_check

View File

@ -37,18 +37,20 @@ describe "Test Drive", :js => true, :type => :feature, :capybara_feature => true
find('.teacher-search-result[data-teacher-id="' + teacher_user.id + '"] .try-test-drive').trigger(:click)
# no longer true
# TryTestDriveDialog shows
find('.purchase-testdrive-now').trigger(:click)
#find('.purchase-testdrive-now').trigger(:click)
fill_in 'card-number', with: '4111111111111111'
fill_in 'expiration', with: '11/2016'
fill_in 'cvv', with: '111'
fill_in 'zip', with: '78759'
select_test_drive(4)
find('.purchase-btn').trigger(:click)
fill_out_single_lesson
fill_out_payment
# we tell user they have test drive purchased, and take them to the teacher screen
find('#banner h1', text: 'Test Drive Purchased')
find('#banner .dialog-inner', text: "You have purchased #{4} TestDrive credits and have used 1 credit it to request a JamClass with #{teacher_user.name}")
# dismiss banner
find('a.button-orange', text:'CLOSE').trigger(:click)
@ -58,35 +60,12 @@ describe "Test Drive", :js => true, :type => :feature, :capybara_feature => true
lesson_package_purchase.lesson_package_type.is_test_drive?.should be_true
lesson_package_purchase.lesson_payment_charge.should_not be_nil
user.reload
user.remaining_test_drives.should eql 4
user.remaining_test_drives.should eql 3
#lesson_package_purchase.amount_charged.should eql 49.99
user.sales.count.should eql 1
sale = user.sales.first
sale.recurly_total_in_cents.should eql 5411
# the spec says take them back to search; there is some wasted effort here by the student; they have to click the teacher 2x. Ok?
find('.teacher-search-result[data-teacher-id="' + teacher_user.id + '"] .try-test-drive').trigger(:click)
find('h2', text: 'book testdrive lesson')
find('.booking-info', text: '4 TestDrive lesson credits')
# book the lesson
fill_in "slot-1-date", with: "Sun Apr 17 2016"
#find('.slot.slot-1 input.hasDatepicker').trigger(:click)
# click 4-6
find('td a', text: '17').trigger(:click)
#find('.slot.slot-2 input.hasDatepicker').trigger(:click)
# click 4-7
fill_in "slot-2-date", with: "Mon Apr 18 2016"
find('td a', text: '18').trigger(:click)
fill_in 'user-description', with: 'abc def dog neck'
sleep 3
find('a.book-lesson-btn', text: 'BOOK TESTDRIVE LESSON').trigger(:click)
find('h2', text: 'my lessons')
@ -112,8 +91,6 @@ describe "Test Drive", :js => true, :type => :feature, :capybara_feature => true
find('h2', text: 'book testdrive lesson')
find('.booking-info', text: '3 TestDrive lesson credits')
# dismiss banner
find('a.button-orange', text:'CLOSE').trigger(:click)
# approve by teacher:
teacher_approve(lesson_session1)

View File

@ -28,4 +28,90 @@ def teacher_approve(lesson_session)
find('.schedule.button-orange').trigger(:click)
visit "/client#/jamclass"
find('tr[data-lesson-session-id="' + lesson_session.id + '"] .displayStatusColumn', text: 'Scheduled')
end
end
def fill_out_single_lesson
find('h2', text: 'book testdrive lesson')
find('.booking-info', text: 'If you need to cancel')
# book the lesson
fill_in "slot-1-date", with: "Sun Apr 17 2016"
#find('.slot.slot-1 input.hasDatepicker').trigger(:click)
# click 4-6
find('td a', text: '17').trigger(:click)
#find('.slot.slot-2 input.hasDatepicker').trigger(:click)
# click 4-7
fill_in "slot-2-date", with: "Mon Apr 18 2016"
find('td a', text: '18').trigger(:click)
fill_in 'user-description', with: 'abc def dog neck'
sleep 3
find('a.book-lesson-btn', text: 'BOOK TESTDRIVE LESSON').trigger(:click)
end
def fill_out_payment
fill_in 'card-number', with: '4111111111111111'
fill_in 'expiration', with: '11/2016'
fill_in 'cvv', with: '111'
fill_in 'zip', with: '78759'
find('.purchase-btn').trigger(:click)
end
def select_test_drive(count = 4)
find(".button-orange.select-#{count}").trigger(:click)
end
def create_stripe_token(exp_month = 2017)
Stripe::Token.create(
:card => {
:number => "4111111111111111",
:exp_month => 2,
:exp_year => exp_month,
:cvc => "314"
},
).id
end
def testdrive_lesson(user, teacher, slots = nil)
if slots.nil?
slots = []
slots << FactoryGirl.build(:lesson_booking_slot_single)
slots << FactoryGirl.build(:lesson_booking_slot_single)
end
if !user.stored_credit_card
token = create_stripe_token
user.payment_update({token: token, zip: '78759'})
user.save!
user.stored_credit_card.should be_true
end
booking = LessonBooking.book_test_drive(user, teacher, slots, "Hey I've heard of you before.")
#puts "BOOKING #{booking.errors.inspect}"
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
booking.card_presumed_ok.should be_true
if user.most_recent_test_drive_purchase.nil?
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
end
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
lesson.errors.any?.should be_false
lesson.reload
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson
end